概要
iOS13からのページシート(ハーフモーダルちっくな標準のモーダル)で、ユーザにプルダウンで閉じられたくない場合にどのようにするかというのを書いておきます。
手順は
- iOS13からの
UIViewController
のフラグvar isModalInPresentation: Bool
をtrueにすると閉じられないようにできる -
var viewWillLayoutSubviews: Bool
がtrueの際にUIAdaptivePresentationControllerDelegate
のメソッドでユーザが閉じようとした際に自前のアラートを表示するようにできる
これについてはAppleのサンプルコードが下記にあります
このコードはWWDC19のセッション「Modernizing Your UI for iOS 13」で解説されていました
詳しく
最小限のコードで
class EditViewController: UIViewController, UIAdaptivePresentationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// フラグをtureにするとpull down時に下記delegateが動作する
isModalInPresentation = true
}
func presentationControllerDidAttemptToDismiss(_ presentationController: UIPresentationController) {
// UIAlertControllerで閉じてよいか自前で表示する
...
}
その他
やりたいことはそこで終わりですが、気になったことがあるので書いておきます
サンプルコードにある編集中を示すやり方
サンプルコードではUITextView
が変更されたかどうか、つまりisModalInPresentation: Bool
をtrueにしたりfalseにするタイミングをUIViewController
のviewWillLayoutSubviews()
でやっています。
これはもし複数UITextView
がある場合に無駄がないようにしているのだと思います。
override func viewWillLayoutSubviews() {
// Ensure textView.text is up to date
textView.text = editedText
// If our model has unsaved changes, prevent pull to dismiss and enable the save button
let hasChanges = self.hasChanges
isModalInPresentation = hasChanges
saveButton.isEnabled = hasChanges
}
これ、テキストが変更されたらeditedText
プロパティを書き換えてそこのdidSet
でsetNeedsLayoutさせてViewのレイアウトを更新するタイミングを要求しています。それが次。
var editedText = "" {
didSet {
viewIfLoaded?.setNeedsLayout()
}
}
こうすることで流れとして次のようにテキストの値を変えてフラグを変更しています。
- ユーザがUITextViewでテキストを変更
- UITextViewDelegateでプロパティ
editedText
を書き換え-
editedText
が書き換わるとviewWillLayoutSubviews()
が動作- (
textView.text = editedText
でUITextViewを上書き) -
isModalInPresentation
をtrueかfalseに
- (
-
- UITextViewDelegateでプロパティ
気付きとしてはviewIfLoaded?.setNeedsLayout()
なんて短い時間に何回読んでもそれらがまとめて実行されるので、もし複数UITextView
がある場合もフラグisModalInPresentation
の変更が無駄に書き換わらないのでしょう。hasChanges
フラグはUITextView
ごとに必要になるでしょうが、その結果のフラグはまとめて切り替えているというやり方な気がします。
-
hasChanges
: ガンガン変わっていい -
isModalInPresentation
: ガンガン変わらないようにviewWillLayoutSubviews
のタイミングで書き換えられている
presentationControllerShouldDismiss
サンプルコードにはありませんが、
「Modernizing Your UI for iOS 13」ではvar viewWillLayoutSubviews: Bool
がfalseでも、デリゲートoptional func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool
でfalseを返せば閉じるのをブロックできる、というような説明をしています。
つまり、編集中ではないのでvar isModalInPresentation: Bool
をfalseにするとしても、アニメーション中などはoptional func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool
でfalseにしておくことで、プルダウンさせてもアラートを表示させられるよ、ということでしょう...。