iOS 15からハーフモーダルっぽい表示が標準APIを使ってできるようになりました。この記事ではその「ハーフモーダル」について紹介しているCustomize and resize sheets in UIKitという動画を紹介してみます。
概要
従来の場合
今までモーダルは全画面表示でした(スクショは.pageSheet
の場合)。
iOS 15 〜
iOS 15では「medium」モードにすると画面の半分だけを覆うシートが作成できるようになりました。
landscapeやiPadのときの表示はこのようになります。
detentsとは
まず最初にdetents
というものを理解する必要があります。detents
とはシートが自然に止まる高さのことを表し、.medium()
と.large()
が用意されています。
コードで書くとこんなかんじです。この指定により、シートの覆い具合を調整することができます。まずviewController
のsheetPresentationController
を取得します。このシートに対しdetents
を設定してあげると、サイズの指定ができます。デフォルトでは.large()
のみが設定された状態だと解釈されます。設定とその結果の対応は以下の通りです。
-
.large()
のみ- フルサイズのシートが表示される(リサイズ不可)
-
.medium()
と.large()
の両方- mediumとlargeでリサイズ可能なシートが表示される
-
.medium()
のみ- ハーフサイズのシートが表示される(リサイズ不可)
detentsを用いた実装例
従来の場合
例えば写真ピッカーは次のような実装をして表示していたと思います。
detentsでサイズ指定
iOS 15から、例えば以下のコード、表示ができます。
-
PHPickerViewController
のsheetPresentationController
を取得してdetents
に.medium()
と.large()
を設定- リサイズ可能なシートを表示する
- ピッカーの
didFinishPicking
デリゲートからdismiss行を削除- 写真を選択し終わってもシートが完全に閉じないようにする
これにより、シートを半分にした状態で写真を選択することも、ドラッグしてフルサイズにしたシートから写真を選択することもできます。
スクロール制御
ただし、このままだと上にスクロールした時にシート自体もスクロールしてフルサイズになってしまい、「スクロールすると、シートは半分のままで、ピッカー内の写真一覧だけがスクロールされる」状態にならなくなってしまいます。バーをドラッグした時にシートのサイズが変わるようにするにはprefersScrollingExpandsWhenScrolledToEdge
をfalse
にする必要があります。
選択時のサイズ指定
次の改善ポイントは写真選択時のアクションです。.large()
の状態で写真を選択しても、選択された写真が表示されるエリアは見れないので、ちゃんと写真が選択されたのかその場でわかりません。そこで、写真が選択された時はシートが.medium()
の大きさになるよう変更してみます。デリゲートメソッド内でselectedDetentIdentifier
を.medium
に設定します。
リサイズ時にアニメーションをつける
写真選択時に.large()
から.medium()
に変更できたのはいいものの、このままだとアニメーションがないのでいきなりサイズが変わってしまいます。次のようにanimateChanges
ブロックで囲むと、自然なアニメーションをつけることができます。
dimmingを消す
選択された写真エリアが薄暗いので、dimmingを解除してみます。smallestUndimmedDetentIdentifier
の設定をいじることで解除が可能です。このプロパティはデフォルトnil
で、すべてのdetentsでdimmingがかかっています。dimmingを解除したい場合はdimmingさせたくない最小のdetentsを設定します。
このプロパティは視覚的に「薄暗さ」を取り除くだけではなく、シート外のコンテンツにアクセスできるような「ノンモーダル状態」を作ることができます。ので、.medium()
の時に写真選択エリアに触れて操作することもできます。
キーボードとの併用
.medium()
の時にキーボードを表示すると、自動で.large()
サイズになります。キーボードが消えると.medium()
サイズに戻ります。
視覚的なカスタマイズ
detents
を操作する以外のところでもいろいろできます。
landscape時のシートサイズ
iOS 15からは、シートの下端だけが画面の縁についている状態で表示できるprefersEdgeAttachedInCompactHeight
が追加されました。これをtrue
にするとSafe Areaと同じ幅のシートを表示できます。
presentedViewControllers
のpreferredContentSize
に沿った幅のシートにしたい場合は、widthFollowsPreferredContentSizeWhenEdgeAttached
をtrue
に設定します。preferredContentSize
を設定することで、幅をさらにカスタマイズすることもできます。
grabberの表示非表示
prefersGrabberVisible
の設定で、grabberの表示非表示を操作することができます。
シートのリサイズが可能であることがわかりづらい場合にはgrabberを表示すると役に立ちます。
シートのradius変更
iPadでポップオーバーを表示する場合
写真ピッカーをポップオーバーにするにはまずmodalPresentationStyle
に.popover
を設定します。次に、先程のようにsheetPresentationController
を取得するのではなく、popoverPresentationController
を取得します。ポップオーバーのsourceをbarButtonItem
に設定し、adaptiveSheetPresentationController
というポップオーバーの新しいプロパティを取得します。このプロパティはポップオーバーがcompactサイズのクラスに適応するシートのインスタンスを返してくれます。
これで写真ボタンをタップするとポップオーバーに写真ピッカーが表示され、領域のサイズを小さくすると、.medium()
サイズのシートが表示されるようになりました。
ただし、このままだと.large()
状態で写真を選択した時に自動で.medium()
サイズになってくれません。popoverPresentationController
のadaptiveSheetPresentationController
を取得して先程と同じように設定する必要があります。
まとめ
- iOS 15からハーフモーダルっぽいUIが標準APIを使って作れるようになったよ
- ちょこちょこカスタマイズできるよ