はじめに
こんにちは、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を改善するなり、もっといい方法があるといいなと思いっています。
修正・意見があればコメントお待ちしています。参考になれば幸いです!