ハイライト
UIKit シートに iOS 15 が導入されました
UISheetPresentationController、中型/大型のダブルハイト ドッキング (戻り止め)、非モーダル インタラクション、カスタムの丸いコーナーとグラバーをサポートし、ハーフスクリーン カード スタイル UI をシステム レベルのネイティブ機能にします。
主な内容
以前は、iOS で半画面のドラッグ可能なカードを作成したいときは、基本的にカスタム トランジション アニメーションのセットを自分で作成する必要がありました。システムに組み込まれているシートの高さは全画面のみです。ユーザーが写真を選択したり設定を変更したりすると、インターフェイス全体が覆われ、操作が完了した後にユーザーはインターフェイス全体を再度開く必要があります。
iOS 15 では、カスタマイズ API の完全なセットが Sheet に追加されます。中心となるアイデアは、シートの状態が「全画面」の 1 つだけではなくなり、複数の「戻り止め」を切り替えることができるということです。システムは、中 (画面の約半分) と大 (全画面) の 2 つのドッキング ポイントを事前に定義します。シートで両方の高さをサポートすることも、一方だけを維持することもできます。
さらに重要なのは、薄暗くなったビューを削除できることです。これは、シートが空中に浮かんでいても、ユーザーはその背後にあるコンテンツを操作できることを意味します。たとえば、画像セレクターは半画面モードで開きます。ユーザーが写真をクリックすると、その後ろのポストカードに写真が直接表示されます。シートは開いたままになり、いつでも別の画像に変更できます。
シート コントローラーの取得
(01:36)
シートをカスタマイズする最初のステップは、UISheetPresentationController例。入手方法とpopoverPresentationController同じ: 存在する前にターゲットのビュー コントローラーを読み取りますsheetPresentationController財産。
if let sheet = viewController.sheetPresentationController {
// Customize the sheet
}
present(viewController, animated: true)
キーポイント:
sheetPresentationControllerだけでmodalPresentationStyleのために.formSheetまたは.pageSheetnil 以外を返します- デフォルトは
.pageSheetなので、ほとんどの場合は直接読んでください。
ストップを設定する
(02:21)
detentsプロパティは、シートがサポートする高さを決定します。デフォルトは[.large()], そのため、設定されていない場合は全画面表示になります。
if let sheet = picker.sheetPresentationController {
sheet.detents = [.medium(), .large()]
}
present(picker, animated: true)
3 つの一般的な構成:
[.large()]- デフォルトの動作、全画面シート[.medium(), .large()]— ドラッグして半画面と全画面を切り替えます[.medium()]— 半画面を修正し、全画面にドラッグできない
ノンモーダル画像セレクター
(03:27)
実際のシナリオを見てみましょう。PHPickerViewController画像を選択します。iOS 14の書き方は全画面ピッカーを表示して選択後に閉じるというものです。iOS 15 は半画面に永続的に変更できます。
func showImagePicker() {
let picker = PHPickerViewController()
picker.delegate = self
if let sheet = picker.sheetPresentationController {
sheet.detents = [.medium(), .large()]
}
present(picker, animated: true)
}
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
// assign result to imageView.image
// dismiss は呼び出さない
}
キーポイント:
detentsに設定[.medium(), .large()]、ピッカーは半画面モードで開きます- 取り除く
dismiss(animated:)の場合、ピッカーは開いたままになり、ユーザーは画像の選択を続けることができます。 - ユーザーは引き続きシートを全画面にドラッグして、さらに多くの写真を閲覧できます
自動スクロール拡張を無効にする
(05:34)
デフォルトの動作では、スクロール ビューが上にスクロールし、下にスクロールし続けると、シートが自動的に大きく拡大されます。画像ピッカーのようなシナリオでは、スクロールがシートの高さではなくコンテンツのみに影響を与えるようにしたい場合があります。
if let sheet = picker.sheetPresentationController {
sheet.detents = [.medium(), .large()]
sheet.prefersScrollingExpandsWhenScrolledToEdge = false
}
キーポイント:
prefersScrollingExpandsWhenScrolledToEdgeデフォルトは true- falseに設定した後は、シートの上部にあるバーをドラッグすることによってのみ高さを変更できます
プログラムによる停止の切り替え
(06:12)
ユーザーが写真を選択した後、シートを自動的に中間の高さに縮小します。これにより、フィードバックが提供されるだけでなく、その背後にあるコンテンツも表示されます。
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
// assign result to imageView.image
if let sheet = picker.sheetPresentationController {
sheet.animateChanges {
sheet.selectedDetentIdentifier = .medium
}
}
}
キーポイント:
selectedDetentIdentifier制御電流停止- 必ず使用してください
animateChangesラップしない場合、スイッチにはアニメーションがありません。 animateChangesスタック内の他のシートのリンクされたアニメーションは同時に処理されます。
背景マスクを削除する
(07:12)
薄暗いビューを削除することが、ノンモーダル インタラクションを実現する鍵となります。
if let sheet = picker.sheetPresentationController {
sheet.detents = [.medium(), .large()]
sheet.prefersScrollingExpandsWhenScrolledToEdge = false
sheet.smallestUndimmedDetentIdentifier = .medium
}
キーポイント:
smallestUndimmedDetentIdentifier中程度に設定すると、中程度の高さではマスクが表示されなくなります。- ユーザーがドラッグするとマスクがフェードインします
- マスクを削除すると、ユーザーはシートの内側と外側のコンテンツを同時に操作できるようになります。
横画面とビジュアルカスタマイズ
(08:44)
iOS 13 のシートは、横向きモードではデフォルトで全画面表示になります。iOS 15 では、下部スナップの別の外観が提供されます。
if let sheet = fontPicker.sheetPresentationController {
sheet.prefersEdgeAttachedInCompactHeight = true
sheet.widthFollowsPreferredContentSizeWhenEdgeAttached = true
}
グラバーインジケーターバーを表示:
if let sheet = fontPicker.sheetPresentationController {
sheet.prefersGrabberVisible = true
}
カスタムの丸い角:
if let sheet = fontPicker.sheetPresentationController {
sheet.preferredCornerRadius = 20.0
}
キーポイント:
prefersEdgeAttachedInCompactHeight全画面ではなく、横画面の下部をシートで吸着させるwidthFollowsPreferredContentSizeWhenEdgeAttached幅をそれに従うようにするpreferredContentSize- システムは、積み重ねられたシートの角の丸みの一貫性を自動的に維持します。
ポップオーバーはカスタマイズされたシートに適応します
(10:05)
iPad でポップオーバーを使用すると、iPhone では自動的にカスタマイズされたシートになります。
func showImagePicker(_ sender: UIBarButtonItem) {
let picker = PHPickerViewController()
picker.delegate = self
picker.modalPresentationStyle = .popover
if let popover = picker.popoverPresentationController {
popover.barButtonItem = sender
let sheet = popover.adaptiveSheetPresentationController
sheet.detents = [.medium(), .large()]
sheet.prefersScrollingExpandsWhenScrolledToEdge = false
sheet.smallestUndimmedDetentIdentifier = .medium
}
present(picker, animated: true)
}
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
if let sheet = picker.popoverPresentationController?.adaptiveSheetPresentationController {
sheet.animateChanges {
sheet.selectedDetentIdentifier = .medium
}
}
}
キーポイント:
adaptiveSheetPresentationControllerコンパクトサイズクラスのポップオーバーのアダプティブシートを入手- 設定方法は通常のシートと全く同じです
- デリゲートメソッドも渡します
popoverPresentationController?.adaptiveSheetPresentationControllerアクセス
##詳細
ディテント機構の詳しい説明
UISheetPresentationController.Detentこれは iOS 15 で導入された新しいタイプで、Sheet の自然なドッキング高さを表します。現在、システムには 2 つの事前定義値が用意されています。
.medium()— 全画面の高さの約 50%.large()— 画面全体の高さ
detentsプロパティは配列であり、順序は機能に影響しません。selectedDetentIdentifier現在アクティブなストップを表し、実行時にコードを通じて切り替えることができます。
アニメーションとインタラクションの詳細
animateChangesが重要な方法です。これはクロージャを受け入れ、クロージャ内の Sheet プロパティを変更するとスムーズなアニメーションが生成されます。このメソッドは、基になるシートのズームリバウンドを含む、シート スタック全体のアニメーションを調整します。
キーボード回避機能も組み込まれています。シートが中程度の高さのときに、内側のテキスト ボックスにフォーカスが当たってキーボードがポップアップすると、シートはキーボードを避けるために自動的に上方に拡張され、キーボードが格納された後は自動的に元に戻ります。
完全な構成例
上記の機能をすべて組み合わせると、一般的な非モーダル イメージ セレクターの構成は次のようになります。
func showImagePicker() {
let picker = PHPickerViewController()
picker.delegate = self
if let sheet = picker.sheetPresentationController {
// 半画面と全画面をサポートする
sheet.detents = [.medium(), .large()]
// スクロールしても自動的に展開しない
sheet.prefersScrollingExpandsWhenScrolledToEdge = false
// 半画面時は遮蔽を表示せず、背後のコンテンツとの操作を許可する
sheet.smallestUndimmedDetentIdentifier = .medium
// grabber を表示する
sheet.prefersGrabberVisible = true
// 角丸をカスタマイズする
sheet.preferredCornerRadius = 20.0
}
present(picker, animated: true)
}
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
// 選択された画像を処理する
if let result = results.first {
result.itemProvider.loadObject(ofClass: UIImage.self) { image, error in
DispatchQueue.main.async {
self.imageView.image = image as? UIImage
}
}
}
// 選択後に自動で半画面へ戻す
if let sheet = picker.sheetPresentationController {
sheet.animateChanges {
sheet.selectedDetentIdentifier = .medium
}
}
}
キーポイント:
detents利用可能な高さを定義するprefersScrollingExpandsWhenScrolledToEdgeスクロール動作を制御するsmallestUndimmedDetentIdentifier制御マスク範囲animateChangesプロパティの変更をラップしてアニメーション化する- 呼ばれていない
dismiss、シートを永続的に保持します
重要ポイント
- 半画面常駐ツールパネルを作成
- 対処方法: 描画アプリやメモ取りアプリでは、ツールバーを中程度の戻り止めシートにして、ユーザーがツールを選択したときにメイン キャンバスが常に表示されるようにします。
- 実行する価値がある理由: 以前は、ツールバーは下部の固定スペースを占有するか、全画面表示されてクリエイティブ フローを中断していました。ハーフスクリーンシートはスペースをとらず、中断もありません
- 開始方法: を使用します
detents = [.medium()]+smallestUndimmedDetentIdentifier = .medium非モーダルツールパネルの実装
- 折りたたみ可能な詳細ページを作成
- 対処法: 地図アプリでは、位置の詳細を表示するために中/大の二重戻り止めを使用します。デフォルトでは、基本情報は半分の画面に表示されます。引き上げると完全な内容が表示されます。
- 実行する価値がある理由: System Map App はこの対話モデルを検証しました。使用
UISheetPresentationControllerカスタムトランジションを使用せずに直接再現可能 - 開始方法:
detents = [.medium(), .large()]+prefersGrabberVisible = true、ユーザーはドラッグして切り替えることができます
- クロスデバイスに適応するポップアップ ウィンドウを作成する
- 対処法: iPad でポップオーバーを使用すると、iPhone では自動的にカスタマイズされたシートになります。 1 つのコード セットで両方のデバイスに適応できます。
- 実行する価値がある理由:
adaptiveSheetPresentationControllerポップオーバーのコンパクトな適応動作をカスタマイズ可能にし、2 セットのロジックを作成する必要性を排除します - 開始方法: 設定
modalPresentationStyle = .popover、次に設定しますpopover.adaptiveSheetPresentationController
- リアルタイム プレビュー用の構成パネル
- 対処方法: 写真編集アプリやフィルターアプリでは、パラメータを調整するためのパネルが半画面シートで開き、調整中にメイン画像がリアルタイムで変化します。
- 実行する価値がある理由: ディミングを削除すると、ユーザーはパネルとメイン画像を同時に見ることができるため、パラメーターを調整するエクスペリエンスがより直感的になります。
- 開始方法:
smallestUndimmedDetentIdentifier = .medium+ 無視されず、パラメーターの変更は背後のビューに直接反映されます。
- 既存のカスタム カードを置き換える
- 何をすべきか: 現在使用している場合
UIViewControllerトランジションをカスタマイズしてハーフスクリーン カードを実装し、UISheetPresentationController - 実行する価値がある理由: システムによって実装されるジェスチャ、アニメーション、およびキーボード回避がより完全になり、コード量が数百行から十数行に削減されました。
- 開始方法: カスタム遷移コードを削除し、現在の状態で設定します。
sheetPresentationControllerの特性
関連セッション
- UIKit の新機能 — Sheet を含むすべてのアップデートを含む、UIKit の年次新機能の概要
- Mac 用 UIKit について — Mac でのシートの動作の違いについて学びます
- インクルーシブ デザインのプロセス — アクセシビリティ エクスペリエンスに対するノンモーダル インタラクションの影響
コメント
GitHub Issues · utterances