View in English

  • メニューを開く メニューを閉じる
  • Apple Developer
検索
検索を終了
  • Apple Developer
  • ニュース
  • 見つける
  • デザイン
  • 開発
  • 配信
  • サポート
  • アカウント
次の内容に検索結果を絞り込む

クイックリンク

5 クイックリンク

ビデオ

メニューを開く メニューを閉じる
  • コレクション
  • トピック
  • すべてのビデオ
  • 利用方法

WWDC21に戻る

ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。

  • 概要
  • トランスクリプト
  • コード
  • UIKitのシートのカスタマイズとリサイズ

    UIKitでレイヤー構造のカスタマイズしたシートエクスペリエンスを作成する方法を紹介します。ここでは、Appでノンモーダルなエクスペリエンスを構築し、シートの上と下の両方のコンテンツを同時に操作できるようにする方法を探ります。また、シートサイズのカスタマイズ、グラバーコントロールの表示・非表示、App内でのポップオーバーとカスタマイズシートの適応についても説明します。 このセッションを最大限に活かしていただくためには、WWDC19の9:45時点からの「iOS 13のためのUIの近代化」のプレゼンテーション部分をご確認になることをお勧めします。

    リソース

    • Customizing and resizing sheets in UIKit
    • UIKit
      • HDビデオ
      • SDビデオ

    関連ビデオ

    WWDC22

    • UIKitの最新情報

    WWDC21

    • App内の写真アクセスの改善

    WWDC19

    • iOS 13向けにUIをモダナイズする
  • このビデオを検索

    ♪ ♪ こんにちは! 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
              }
          }
      }

Developer Footer

  • ビデオ
  • WWDC21
  • UIKitのシートのカスタマイズとリサイズ
  • メニューを開く メニューを閉じる
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    メニューを開く メニューを閉じる
    • アクセシビリティ
    • アクセサリ
    • App Extension
    • App Store
    • オーディオとビデオ(英語)
    • 拡張現実
    • デザイン
    • 配信
    • 教育
    • フォント(英語)
    • ゲーム
    • ヘルスケアとフィットネス
    • アプリ内課金
    • ローカリゼーション
    • マップと位置情報
    • 機械学習
    • オープンソース(英語)
    • セキュリティ
    • SafariとWeb(英語)
    メニューを開く メニューを閉じる
    • 英語ドキュメント(完全版)
    • 日本語ドキュメント(一部トピック)
    • チュートリアル
    • ダウンロード(英語)
    • フォーラム(英語)
    • ビデオ
    Open Menu Close Menu
    • サポートドキュメント
    • お問い合わせ
    • バグ報告
    • システム状況(英語)
    メニューを開く メニューを閉じる
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles(英語)
    • フィードバックアシスタント
    メニューを開く メニューを閉じる
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program(英語)
    • News Partner Program(英語)
    • Video Partner Program(英語)
    • セキュリティ報奨金プログラム(英語)
    • Security Research Device Program(英語)
    Open Menu Close Menu
    • Appleに相談
    • Apple Developer Center
    • App Store Awards(英語)
    • Apple Design Awards
    • Apple Developer Academy(英語)
    • WWDC
    Apple Developerアプリを入手する
    Copyright © 2025 Apple Inc. All rights reserved.
    利用規約 プライバシーポリシー 契約とガイドライン