ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
UIKitのシートのカスタマイズとリサイズ
UIKitでレイヤー構造のカスタマイズしたシートエクスペリエンスを作成する方法を紹介します。ここでは、Appでノンモーダルなエクスペリエンスを構築し、シートの上と下の両方のコンテンツを同時に操作できるようにする方法を探ります。また、シートサイズのカスタマイズ、グラバーコントロールの表示・非表示、App内でのポップオーバーとカスタマイズシートの適応についても説明します。 このセッションを最大限に活かしていただくためには、WWDC19の9:45時点からの「iOS 13のためのUIの近代化」のプレゼンテーション部分をご確認になることをお勧めします。
リソース
関連ビデオ
WWDC22
WWDC21
WWDC19
-
ダウンロード
♪ ♪ こんにちは! UIKit チームのエンジニアのRussellです iOS 13でシートの外観が洗練され 電話にも登場しました 世界共通のプルして閉じるも追加されました もっと詳しく知るためにこの動画をごらんください iOS13の最新のUIは9分45秒から始まります プレゼンテーションで紹介しています iOS 15でシートにカスタマイズオプションを 多く追加して基礎を築いたので これまでにない楽しい使い方ができます ミディアムディテントのサポートも追加し 画面半分だけカバーする 縦にリサイズ可能なシートを 作成できます
ビューの減光も解除できるようになったことで ユーザーはシートの背後の コンテンツとインタラクション出来る様に 非モーダル型のUIが構築できるようになりました 次に新しい視覚オプションで 横向きの電話をフルスクリーンではない 外観にする方法などを紹介します 最後にシームレスに適応するUIを設定する方法で レギュラーサイズのクラスのポップオーバー コンパクトサイズのクラスでカスタマイズされた シートをシームレスに適応させる UIの設定を紹介します
そこでデジタルポストカードを作成するための サンプルAppを作りました このビデオと一緒にご覧ください それぞれのポストカードは写真だけでなく テキストやフォントもカスタマイズできます シートをカスタマイズする前に シートを手に入れなければなりません シートは新しい UIPresentationController のサブクラスの UISheetPresentationController の インスタンスです すべてのカスタマイズオプションは このクラスのプロパティとして公開されます このクラスのインスタンスを 取得する典型的な方法は 表示する前にビューコントローラの sheetPresentationControllerプロパティを 読み取ることです このメソッドはデフォルトでビューコントローラの modalPresentationStyleが フォームシートやページシートで ある限りnilではないインスタンスを返します
ここからインスタンスに色々プロパティを設定して カスタマイズできます これはビューコントローラの popoverPresentationControllerの設定と同じです 次はディテントに入ります
ディテントとは? ディテントとはシートが自然に止まる高さのことで 完全に展開したシートフレームの 一部として定義されます 展開されたフレームは視覚化されます iPhoneや iPad にシートを使用したことがある方は 見覚えがあるでしょう iOS 15ではシステム定義で2つのディテントを公開し シート高の約半分のミディアムディテントと フルの拡張シートの高さの ラージディテントがあります
シートでサポートするディテントの指定は ディテントプロパティで必要なディテントを 設定するだけ プロパティの既定値はラージディテントだけで 全く設定しなければ 標準のフル高シートが表示されます
これをミディアムとラージにに設定すると ミディアムとラージの間で サイズ変更可能なシートが作成されます しかしミディアムディテントだけの 配列にも設定もできます これによりシートはミディアムとの高さになり 全高にはリサイズできないシートになります これをサンプルAppに適用してみます iOS 14 SDKを対象に標準的なシートに 表示するコードを紹介します ボタンがタップされると最初の機能が呼び出され イメージピッカーを作成しピッカーのデリゲートを selfに設定しピッカーを表示します
画像選択するとピッカーはFinishPickingを 出すことで選択された画像を イメージビューに設定しピッカーを閉じます
それでは走らせてみましょう 写真ボタンをタップすると フォトピッカーがApp全体をカバーします 最近の旅行はとってもバーチャルです 写真を選ぶとフォトピッカーが閉じ ポストカードで選んだ写真を表示します 別の写真を選びたい場合は この流れをもう一回します 写真のライブラリとポストカードの 同時表示は 本当に素晴らしい機能になります ディアムディテントではそれが可能です
以前のコードと同じですが いくつかの変更があります ピッカーを表示する前に sheetPresentationControllerにアクセスして ディテントをミディアムとラージに設定します pickerのdidFinishPicking デリゲートコールバックで フォトピッカーを閉じる 行を削除しました 写真選択でフォトピッカーを終了させないためです
今これを実行し写真ボタンをタップします ライブラリーは半分の高さになります 写真を選んでできあがりです ポストカードで表示しライブラリは下にあります 別の写真で試したい場合は 1回タップで簡単にできます ディテントアレイにはラージディテントも含まれ バーをドラッグしてシート サイズを最大に変更します
スクロールビューは上にスクロールしているので スクロールビューをスクロールすると シートも拡大します 「シェアシート」のようなアクションのシートでは より高度なアクションを段階的に表示する 優れた機能でリストの下に表示されます しかしフォトライブラリーの例では シートが拡大しないようにバーからドラッグして 明示的にシートのサイズを変更しない限り スクロールしてもポストカードが常に 表示されるようにしたいと思います この代替動作を行うために もうひとつ必要な追加プロパティ: PrefersScrollingExpandsWhenScrolledToEdge デフォルトではこのプロパティはtrueです これをfalseに設定するとスクロールは行いません シートが広げられません
スクロールしても フォトピッカーのサイズは同じです バーをドラッグしてフォトライブラリを 見やすくできます
しかし今タップすると 何が起こるかわかりません 写真をタップするとフォトピッカーが閉じられ 選択の受け入れがはっきり示されます これは以前の動作とは対照的です
写真をタップしたときに シートサイズをミディアムにし 選択が受信されたことを表示し 内容をポストカードに 表示したいと思います プログラムで選択されたディテントの変更で これが実現できます 写真がタップされるたび呼び出される イメージピッカーデリゲートメソッドに戻ると ここにコードを追加して sheetPresentationController を取得しselectedDetentIdentifierを ミディアムに設定できます ではやってみましょう 写真をタップしたときの遷移に注目してください
おっと! 切り替えがとても早くて眉毛が抜けそうです 実際はまったくアニメーションしてません プロパティの設定を sheet.animateChangesブロックで囲むことで トランジションを簡単にアニメーションできます 必要に応じて標準のアニメーションカーブで シートをミディアムディテント までアニメーションさせ ルートシートがスケールアップするなど スタック内のシートもアニメーションさせます
もうひとつの良い点は暗いビューを解除して 選択した写真をフルカラー表示できます そのためにはもうひとつのプロパティの smallestUndimmedDetentIdentifierを 見る必要がありますデフォルトではnilで すべてのディテントが調光されます もし調光を解除したいなら 調光をさせたくない 最小のディテントの 識別子に設定しますミディアムに設定します ピッカーを起動しても
ミディアムディテントでは 調光しないことに気づきます ジャジャーン! ラージサイズに変更すると 調光はフェードインします
このプロパティの使用でシート内のコンテンツや シート外のコンテンツも操作できるため 視覚的に調光を除去するだけでなく 高度な非モーダルエクスペリエンスを 構築することができます さらにフォントピッカーでは
起動中にテキストの範囲を選択し その範囲だけにフォントを適用し 選択範囲を調整し 再びフォントを適用するUIを構築しました 詳しくはサンプルAppをダウンロードしてください ミディアムのシートでは
また中程度の高さのシートでは キーボードの自動回避に 対応しておりここでフォントを検索すると キーボードに考慮してシートが自動的に増えます キーボードが解除されると シートは自動的に戻ります ディテントについての多くの情報でしたが
ここからはシートの 視覚的カスタマイズの 新しいオプションについて 紹介します まずiPhoneの横向きでシート表示のオプションが 追加されました iOS 13ではすべてのシートを 横向きでフルスクリーンにしました 今回シートが画面の下端にのみ貼り付る 別の表示方法を用意しました
この新しい外観を取得するには 単に prefersEdgeAttachedInCompactHeightを trueにしますこの設だけで 常に セーフエリアと同じ幅のシートが得られます シートの幅を presentedViewControllersの 優先コンテンツサイズに preferredContentSize設定は widthFollowsPreferredContentSize- WhenEdgeAttached をtrueに設定します これによりシートのデフォルト幅が狭くなり preferredContentSizeを設定することで この幅をさらにカスタマイズできます これらのプロパティの他に お好みでグラバーを表示できます グラバーは必ずしも必要ではありませんが スクロールしてもシートの サイズが変更されないなど リサイズ可能であることが明らかでない場合 グラバーを表示することで リサイズ可能であることを示すことができます シートのコーナーを見てみましょう コーナーの半径をカスタマイズする機能も 公開しました Appの外観がより丸みを帯びた場合 その美しさに合わせたデザインにするといいです スタックされたコーナーの見た目を一定に保つため フォトピッカーが拡大して ルートシートを押し戻すと ルートシートのコーナーも それに合わせて大きくなります 最後にディテントを使って ipadでシートが作れますが ディテントなどの項目を使ってカスタマイズできる コンパクトなシートに合わせてポップオーバーが 必要になることがよくあります この方法をサンプルAppで試してみましょう iPadでイメージピッカーの ポップオーバーを出すには ちょっと修正が必要です まずmodalPresentationStyleを 設定します 使うのは sheetPresentationController これは今はnilを返してしまうからです modalPresentationStyleはシートではないため popoverPresentationControllerを取得します ポップオーバーのソースをbarButtonItemに設定し 新しいプロパティを取得します adaptiveSheetPresentationControllerで これはポップオーバーがコンパクトサイズ対応で シートのインスタンスを返します 先ほどのシートと同じように設定します
写真のボタンをタップ ポップオーバーフォトピッカーが出ます シーンのサイズを変更すると ミディアムの高さのシートに適応します 使えますね! そしてピッカーを広げて写真を選択すると しまった! 自動的にミディアム高さに リサイズされませんでした 以前できたのに うーん ピッカーの didFinishPickingデリゲートメソッドに戻ります
あ! アダプティブシートを取得するには PresentationControllerを popoverPresentationController上の コードで読み込ませます 写真を選ぶときに気がつくことがあります やったーサイズがミディアムに戻りました ました
さてシートを使ってこれまで簡単に作れなかったv UIを簡単に作成できるさまざまな機能について 説明しました ではAppを見直してメリットのある部分を探して ミディアムハイトシートや ノンモーダルエクスペリエンスや Appにハーフカイトのカスタムカードをお持ちなら 新しく強化したIKit シートに置き換えてください シートを使ってクールな 新しいものを作ってください ご覧いただきありがとうございました
-
-
0:01 - Get a sheet
if let sheet = viewController.sheetPresentationController { // Customize the sheet } present(viewController, animated: true)
-
0:02 - Detents (large only)
if let sheet = picker.sheetPresentationController { sheet.detents = [.large()] } present(picker, animated: true)
-
0:03 - Detents (medium and large)
if let sheet = picker.sheetPresentationController { sheet.detents = [.medium(), .large()] } present(picker, animated: true)
-
0:04 - Detents (medium only)
if let sheet = picker.sheetPresentationController { sheet.detents = [.medium()] } present(picker, animated: true)
-
0:05 - Present image picker in a standard sheet
func showImagePicker() { let picker = PHPickerViewController() picker.delegate = self present(picker, animated: true) } func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { // assign result to imageView.image dismiss(animated: true) }
-
0:06 - Present at medium detent, and don’t dismiss automatically
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 }
-
0:07 - Prevent scrolling from expanding the sheet
func showImagePicker() { let picker = PHPickerViewController() picker.delegate = self if let sheet = picker.sheetPresentationController { sheet.detents = [.medium(), .large()] sheet.prefersScrollingExpandsWhenScrolledToEdge = false } present(picker, animated: true) } func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { // assign result to imageView.image }
-
0:08 - Select medium detent when a photo is picked
func showImagePicker() { let picker = PHPickerViewController() picker.delegate = self if let sheet = picker.sheetPresentationController { sheet.detents = [.medium(), .large()] sheet.prefersScrollingExpandsWhenScrolledToEdge = false } present(picker, animated: true) } func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { // assign result to imageView.image if let sheet = picker.sheetPresentationController { sheet.selectedDetentIdentifier = .medium } }
-
0:09 - Animate selection of medium detent
func showImagePicker() { let picker = PHPickerViewController() picker.delegate = self if let sheet = picker.sheetPresentationController { sheet.detents = [.medium(), .large()] sheet.prefersScrollingExpandsWhenScrolledToEdge = false } present(picker, animated: true) } func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { // assign result to imageView.image if let sheet = picker.sheetPresentationController { sheet.animateChanges { sheet.selectedDetentIdentifier = .medium } } }
-
0:10 - Remove dimming at medium detent
func showImagePicker() { let picker = PHPickerViewController() picker.delegate = self if let sheet = picker.sheetPresentationController { sheet.detents = [.medium(), .large()] sheet.prefersScrollingExpandsWhenScrolledToEdge = false sheet.smallestUndimmedDetentIdentifier = .medium } present(picker, animated: true) } func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { // assign result to imageView.image if let sheet = picker.sheetPresentationController { sheet.animateChanges { sheet.selectedDetentIdentifier = .medium } } }
-
0:11 - iPhone in landscape
if let sheet = fontPicker.sheetPresentationController { sheet.prefersEdgeAttachedInCompactHeight = true sheet.widthFollowsPreferredContentSizeWhenEdgeAttached = true } present(fontPicker, animated: true)
-
0:12 - Show a grabber
if let sheet = fontPicker.sheetPresentationController { sheet.prefersGrabberVisible = true } present(fontPicker, animated: true)
-
0:13 - Customize the corner radius
if let sheet = fontPicker.sheetPresentationController { sheet.preferredCornerRadius = 20.0 } present(fontPicker, animated: true)
-
0:14 - Adapt a popover to a customized sheet
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) }
-
0:15 - Be consistent when using adaptiveSheetPresentationController
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { // assign result to imageView.image if let sheet = picker.popoverPresentationController?.adaptiveSheetPresentationController { sheet.animateChanges { sheet.selectedDetentIdentifier = .medium } } }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。