はじめに
こんにちは、fummicc1です。今回は、iOS13から変更されたmodalPresentationStyleに.pageSheetを設定した場合の挙動を説明した後に、実際にどういうコードを書くのがいいのかを紹介します。
iOS13のページシート
iOS13からiPhoneでページシートの挙動が大きく変わりました。そして、デフォルトでページシートで遷移されるようになりました。
今まで(iOS13以前)は.fullScreenと同じ挙動だったのですが、今回から被さるようなUIになりました。(画像参照)

まず、これによって以下のようなAPIが呼ばれなくなりました。
- viewWillAppear
- viewWillDisappear
これは既存のアプリケーションを破壊する可能性があるので至急対応しているところが多いと思います。
例えば、model遷移先の画面で何か日記やToDoを作成し、saveボタンを押して保存処理をし画面をdismissします。
その場合、従来はviewWillAppearでcollectionViewやtableViewなどのUIの更新処理をかけている場合多いです。
しかし、pageSheetによる遷移ではdismissしてもviewWillAppearは呼ばれないので、日記やToDoを保存した後の状態がUIに反映されません。今回は、これに対応する方法を紹介します。
対応策
1. .fullScreenにする。
modalPresentationStyleに.pageSheetではなく、.fullScreenを指定します。そうすれば既存の手法で話を進められます。
2. .pageSheetのままで実装する。
presentationControllerDidDismissというメソッドがiOS13から追加されているので、それを使用します。
このメソッドは遷移先の画面のインスタンスでpresentationController?.delegate = selfを実装します。このとき、self(遷移元の画面)はUIAdaptivePresentationControllerDelegateに準拠している必要があります。
また、このUIAdaptivePresentationControllerDelegateはpresentationControllerDidDismissを所持しているので以下のように実装します。
func transitionToModalViewController() {
let modalViewController = storyboard?.instantiateViewController(identifier: "ModalViewController") as! ModalViewController
modalViewController.presentationController?.delegate = self // ここがポイント
present(modalViewController, animated: true, completion: nil)
}
extension RootViewController: UIAdaptivePresentationControllerDelegate {
// このメソッドを実装
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
// Modal画面から戻った際の画面の更新処理を行う。 (collectionView.reloadDataなど。)
}
}
これで、ユーザーがModal画面で下からスワイプをしたときにも更新処理ができます。
dismissでModalを閉じるとpresentationControllerDidDismissが呼ばれない。
これの対処法は、ベストプラクティスではないような気もしますが、遷移作の画面でdismissをオーバーライドし、その中でpresentationController.delegate?.presentationControllerDidDismiss?(presentationController)を呼びます。
extension ModalViewController {
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
super.dismiss(animated: flag, completion: completion)
guard let presentationController = presentationController else {
return
}
presentationController.delegate?.presentationControllerDidDismiss?(presentationController)
}
}
これでdismissで画面を戻ってもUIの更新処理が呼ばれます。
最後に
iOS13の変化として、ダークモード対応やSign in with Apple対応などでも大変なのですが、Modalも結構変化して対応するのにコストがかかるなと感じていて、今回の記事も少ないリソースの中での解決策で個人的には満足した実装ではないので、今後APIを改善するなり、もっといい方法があるといいなと思いっています。
修正・意見があればコメントお待ちしています。参考になれば幸いです!