Help us understand the problem. What is going on with this article?

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

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away