search
LoginSignup
47

More than 3 years have passed since last update.

posted at

updated at

iOS13のPageSheetでdismissを呼んだ時にもpresentationControllerDidDismissを呼ぶ話

はじめに

こんにちは、fummicc1です。今回は、iOS13から変更されたmodalPresentationStyle.pageSheetを設定した場合の挙動を説明した後に、実際にどういうコードを書くのがいいのかを紹介します。

iOS13のページシート

iOS13からiPhoneでページシートの挙動が大きく変わりました。そして、デフォルトでページシートで遷移されるようになりました。
今まで(iOS13以前)は.fullScreenと同じ挙動だったのですが、今回から被さるようなUIになりました。(画像参照)
Simulator Screen Shot - iPhone 11 Pro Max - 2019-09-29 at 17.01.29.png

まず、これによって以下のようなAPIが呼ばれなくなりました。

  • viewWillAppear
  • viewWillDisappear

これは既存のアプリケーションを破壊する可能性があるので至急対応しているところが多いと思います。
例えば、model遷移先の画面で何か日記やToDoを作成し、saveボタンを押して保存処理をし画面をdismissします。
その場合、従来はviewWillAppearcollectionViewtableViewなどのUIの更新処理をかけている場合多いです。

しかし、pageSheetによる遷移ではdismissしてもviewWillAppearは呼ばれないので、日記やToDoを保存した後の状態がUIに反映されません。今回は、これに対応する方法を紹介します。

対応策

1. .fullScreenにする。

modalPresentationStyle.pageSheetではなく、.fullScreenを指定します。そうすれば既存の手法で話を進められます。

2. .pageSheetのままで実装する。

presentationControllerDidDismissというメソッドがiOS13から追加されているので、それを使用します。

このメソッドは遷移先の画面のインスタンスでpresentationController?.delegate = selfを実装します。このとき、self(遷移元の画面)UIAdaptivePresentationControllerDelegateに準拠している必要があります。

また、このUIAdaptivePresentationControllerDelegatepresentationControllerDidDismissを所持しているので以下のように実装します。

RootViewController.swift
func transitionToModalViewController() {
    let modalViewController = storyboard?.instantiateViewController(identifier: "ModalViewController") as! ModalViewController
    modalViewController.presentationController?.delegate = self // ここがポイント
    present(modalViewController, animated: true, completion: nil)
}
RootViewController.swift
extension RootViewController: UIAdaptivePresentationControllerDelegate {
    // このメソッドを実装
    func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
        // Modal画面から戻った際の画面の更新処理を行う。 (collectionView.reloadDataなど。)
    }
}

これで、ユーザーがModal画面で下からスワイプをしたときにも更新処理ができます。

dismissでModalを閉じるとpresentationControllerDidDismissが呼ばれない。

これの対処法は、ベストプラクティスではないような気もしますが、遷移作の画面でdismissをオーバーライドし、その中でpresentationController.delegate?.presentationControllerDidDismiss?(presentationController)を呼びます。

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
47