화면을 반만 띄우거나 중간에서 작게 띄우고 싶으면 어떻게 해야할까? 🤔
특정 정보를 띄우거나 사용자 입력을 받기 위한 화면이 전체 화면을 덮지 않고 일부분만 보이는 경우를 상용 앱에서도 심심치 않게 볼 수 있다.
일반적인 모달로 화면이동을 하면 화면 대부분을 덮기 때문에 여러 화면이 필요하다면 잦은 화면 이동으로 사용자에게 피로감을 줄 수 있다.
결론부터 말하자면 특정 위치에 특정 크기에 모달 뷰를 띄울 수 있는 방법은 UIPresentationController를 이용해 커스텀 모달 뷰를 만들어 사용하는 것이다!
우선 공식문서의 내용을 정리하면 다음과 같다.
전환 애니메이션과 뷰컨트롤러의 표시를 관리하는 객체
UIKit은 PresentationController
를 통해 뷰컨트롤러가 표시되는 시간부터 사라질 때까지 프레젠테이션 프로세스에 관한 다양한 측면을 관리합니다. PresentationController
를 사용하면 자체 애니메이션을 추가할 수 있고, 크기 변경 및 화면 표시 관련 측면을 관리할 수 있습니다.
다른 방식으로 프레젠테이션 동작을 수정하려는 경우 사용자 지정 PresentationController
를 제공할 수 있습니다.
뷰 컨트롤러의 transitioning delegate를 통해 커스텀 PresentationController
를 사용할 수 있습니다. UIKit은 뷰컨트롤러가 화면에 표시되는 동안 PresentationController
객체에 참조를 유지합니다.
-> UIViewControllerTransitioning
프레젠테이션 프로세스
- 전환 애니메이션을 통해 새 뷰컨트롤러로 이동하는 작업
- 새 뷰컨트롤러가 표시되는 동안 환경 변경(ex. 기기 회전)에 대한 응답
- 전환 애니메이션을 통해 새 뷰컨트롤러 화면 밖으로 이동하는 작업
Instance Method
presentationTransitionWillBegin()
: 커스텀 뷰를 뷰 계층에 추가하고 해당 뷰의 appearance에 관한 애니메이션 수행
dismissalTransitionWillBegin()
: 뷰컨트롤러의 해제와 관련된 애니메이션을 수행
viewWillTransition(to:with:)
: 커스텀 뷰의 크기 관련 변경을 수행Instance Property
frameOfPresentedViewInContainerView
: 애니메이션이 끝날 때 표시되는 보기에 할당할 프레임
뷰컨트롤러 간의 fixed-length 또는 interactive transition를 관리하는 메서드 집합
custom modal presentation을 사용해 뷰컨트롤러를 표시하려면 modalPresentationStyle
을 custom
으로 설정하고, 이 프로토콜을 준수하는 객체를 transitioningDelegate
프로퍼티에 할당합니다. 해당 뷰컨트롤러를 present하면 UIKit은 뷰컨트롤러를 애니메이팅할 때transitioningDelegate
를 쿼리합니다.
presentationController(forPresented:presenting:source:)
: 뷰컨트롤러 표시 시 뷰 계층을 관리할 커스텀 PresentationController의 delegate에게 요청하는 메서드
optional func presentationController(
forPresented presented: UIViewController,
presenting: UIViewController?,
source: UIViewController
) -> UIPresentationController?
그래서 어떻게 구현하느냐! 실제로 코드로 적용해보면 다음과 같습니다.
// Modal을 띄울 컨트롤러
final class CustomPresentationController: UIPresentationController {
}
우선 모달로 띄울 뷰컨트롤러를 관리할 UIPresentationController
를 정의합니다.
// CustomPresentationController.swift
override var frameOfPresentedViewInContainerView: CGRect {
let screenBounds = UIScreen.main.bounds
// ModalView의 크기
let size = CGSize(width: screenBounds.width,
height: screenBounds.height * 0.25)
// ModalView의 위치
let origin = CGPoint(x: .zero, y: screenBounds.height * 0.75)
return CGRect(origin: origin, size: size)
}
그리고 frameOfPresentedViewInContainerView
를 override하여 모달 뷰를 띄우고 싶은 위치와 크기를 지정해줍니다. 위 코드는 모달 뷰가 아래에서 일부만 보이도록 설정한 예시입니다.
// CustomPresentationController.swift
override func presentationTransitionWillBegin() {
super.presentationTransitionWillBegin()
// 화면 전환하며 뷰가 나타날 때 실행할 코드
}
override func dismissalTransitionWillBegin() {
super.dismissalTransitionWillBegin()
// 화면이 사라질 때 실행할 코드
}
또한 모달 화면이 나타나고 사라질 때 설정해줘야 할 코드를 역시 override 메서드를 통해 설정해줍니다. 일반적으로 화면이 나타나고 사라지는 애니메이션을 추가할 수 있습니다.
final class CustomTransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
func presentationController(forPresented presented: UIViewController,
presenting: UIViewController?,
source: UIViewController) -> UIPresentationController? {
return CustomPresentationController(
presentedViewController: presented,
presenting: presenting
)
}
}
이후 UIViewControllerTransitioningDelegate
을 상속하는 커스텀 델리게이트를 생성하고 presentationController
메서드가 커스텀한 PresentationController를 반환하도록 구현합니다.
final class CustomViewController: UIViewController {
weak var delegate: CustomTransitioningDelegate?
private var customTransitioningDelegate = CustomTransitioningDelegate()
// 뷰 컨트롤러 정의
private func setupModalStyle() {
modalPresentationStyle = .custom
modalTransitionStyle = .crossDissolve
transitioningDelegate = customTransitioningDelegate
}
}
마지막으로 실제로 모달로 띄울 뷰컨트롤러를 정의하고 modalPresentationStyle
를 cutom
으로 설정하고 transitioningDelegate
를 커스텀한 델리게이트로 지정해주면 끝!
private func modalButtonTapped() {
let customViewController = CustomViewController()
customViewController.delegate = self
present(customViewController, animated: true)
}
이후 구현한 모달을 present
해주면 원하는 위치와 크기로 띄워지는걸 확인할 수 있다 👍
예시 화면
++ 실제 사용한 코드는 아래 깃헙에서 참고해주세요! 🙇🏻♀️
Judy-999/ios-BoxOffice
혹시 잘못된 설명이나 코드가 있다면 알려주시면 감사하겠습니다 😊
참고하면 좋은 자료
Custom View Controller Presentation Tips & Tricks