1.はじめに
まずは完成形を。
遷移元の画面上にオーバレイする形でモーダルビューを表示します。
いつもとちょっと違ったモーダルを出したいときに重宝しています。
2.呼び元の ViewController
class PresentationControllerViewController: UIViewController {
@IBAction func openButton(_ sender: UIButton) {
let modalViewController = ModalViewController()
modalViewController.modalPresentationStyle = .custom
modalViewController.transitioningDelegate = self
present(modalViewController, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension PresentationControllerViewController: UIViewControllerTransitioningDelegate {
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return CustomPresentationController(presentedViewController: presented, presenting: presenting)
}
}
openButton(_ sender: UIButton)
はstoryboardで配置したボタンと接続します。モーダルビューを開くための機能をここに実装します。
ポイントは modalViewController.modalPresentationStyle
に .custom
をセットすることです。
UIViewControllerTransitioningDelegate
プロトコルに準拠し、これから作る CustomPresentationController
を返すようにします。
3.モーダルビューを乗せる UIPresentationController
margin
の値を変えることでモーダルビューの大きさを変えることができます。
class CustomPresentationController: UIPresentationController {
// 呼び出し元のView Controller の上に重ねるオーバレイView
var overlayView = UIView()
// 表示トランジション開始前に呼ばれる
override func presentationTransitionWillBegin() {
guard let containerView = containerView else {
return
}
overlayView.frame = containerView.bounds
overlayView.gestureRecognizers = [UITapGestureRecognizer(target: self, action: #selector(CustomPresentationController.overlayViewDidTouch(_:)))]
overlayView.backgroundColor = .black
overlayView.alpha = 0.0
containerView.insertSubview(overlayView, at: 0)
// トランジションを実行
presentedViewController.transitionCoordinator?.animate(alongsideTransition: {[weak self] context in
self?.overlayView.alpha = 0.7
}, completion:nil)
}
// 非表示トランジション開始前に呼ばれる
override func dismissalTransitionWillBegin() {
presentedViewController.transitionCoordinator?.animate(alongsideTransition: {[weak self] context in
self?.overlayView.alpha = 0.0
}, completion:nil)
}
// 非表示トランジション開始後に呼ばれる
override func dismissalTransitionDidEnd(_ completed: Bool) {
if completed {
overlayView.removeFromSuperview()
}
}
let margin = (x: CGFloat(30), y: CGFloat(220.0))
// 子のコンテナサイズを返す
override func size(forChildContentContainer container: UIContentContainer, withParentContainerSize parentSize: CGSize) -> CGSize {
return CGSize(width: parentSize.width - margin.x, height: parentSize.height - margin.y)
}
// 呼び出し先のView Controllerのframeを返す
override var frameOfPresentedViewInContainerView: CGRect {
var presentedViewFrame = CGRect()
let containerBounds = containerView!.bounds
let childContentSize = size(forChildContentContainer: presentedViewController, withParentContainerSize: containerBounds.size)
presentedViewFrame.size = childContentSize
presentedViewFrame.origin.x = margin.x / 2.0
presentedViewFrame.origin.y = margin.y / 2.0
return presentedViewFrame
}
// レイアウト開始前に呼ばれる
override func containerViewWillLayoutSubviews() {
overlayView.frame = containerView!.bounds
presentedView?.frame = frameOfPresentedViewInContainerView
presentedView?.layer.cornerRadius = 10
presentedView?.clipsToBounds = true
}
// レイアウト開始後に呼ばれる
override func containerViewDidLayoutSubviews() {
}
// overlayViewをタップした時に呼ばれる
@objc func overlayViewDidTouch(_ sender: UITapGestureRecognizer) {
presentedViewController.dismiss(animated: true, completion: nil)
}
}