Sheetは、メインコンテンツの上に画面下部に表示され、追加のアクションや情報を提供するUIコンポーネントです。Bottom Sheetは最も一般的な種類で、Material Design 3(Google、2024)のガイドラインで定義されています。ダイアログウィンドウとは異なり、Sheetはバックグラウンドとの操作を完全にブロックせず、collapsedおよびexpanded状態をサポートします。モバイルアプリケーションで便利なナビゲーションを構築するには、Sheetの種類と動作を理解することが必要です。
重要ポイント
Sheetは、画面下部からスライドアップし、現在の画面からユーザーを移動させずに追加コンテンツやアクションを表示するサーフェスレベルのUIコンポーネントです。全画面遷移とは異なり、Sheetはコンテキストを保持します。ユーザーは背景画面を確認でき、Sheetがどこから現れたかを理解できます。
Sheetのアーキテクチャは高さ調節可能なコンテナに基づいています。最小状態(collapsed)では、コンテンツの一部のみが表示され、通常はタイトルまたは小さなパネルだけです。ユーザーはSheetを上にドラッグして展開(expanded)したり、下にスワイプして閉じたりできます。この動作は、紙の束の下から物理的なシートを引き出す様子に似ていることから、この名前が付けられました。
GoogleはMaterial Design 3のガイドライン(2024)で、Sheetの使用に関する2つの主要なシナリオを強調しています。メインツールバーに収まらない追加アクションの表示、および別画面に移動せずにフォームや詳細情報を表示することです。iOSでは、AppleがiOS 15からUISheetPresentationControllerを通じてシステムレベルのSheetサポートを追加し、このパターンの普遍性を証明しています。
Material Designは3種類のBottom Sheetを定義しており、それぞれに独自の動作と適用領域があります。適切なタイプを選択することは、ユーザーエクスペリエンスとプラットフォームガイドラインへの準拠に直接影響します。
標準Bottom Sheetは非モーダルコンポーネントです。ユーザーはSheetを閉じずに背景画面と操作できます。例えば、背景で商品リストを閲覧しながら、ユーザーはBottom Sheetにフィルターを表示し、パネルを閉じずに変更できます。Sheetはコンテンツに応じて高さを調整します(画面の30%~90%)。
標準Bottom Sheetは背景をブロックせず、メインコンテンツとの操作を許可します。これがモーダルSheetとの主な違いであり、モーダルSheetは続行するために必須のアクションが必要です。追加情報がメインシナリオにとって重要でない場合は、標準Sheetを使用してください。
モーダルBottom Sheetは、ユーザーがSheet内でアクションを実行するまで背景画面との操作をブロックします。このようなSheetは常に展開状態で表示され、ボタン、アクション、または下へのスワイプによる明示的な閉じる操作が必要です。Material Designは、ログインフォーム、確認ダイアログ、重要な設定にモーダルSheetを推奨しています。
Androidでは、モーダルBottom SheetはBottomSheetDialogFragmentを介して実装され、自動的に背景をブロックします。iOSでは、.mediumDetentパラメータを持つUISheetPresentationControllerのモーダルバリアントが同様の動作を提供します。モーダルSheetは、コンテキストがユーザーの注意を必要とする場合にのみ使用する必要があります。
拡張Bottom Sheetは標準とモーダルの特性を組み合わせたものです。collapsed状態では非モーダルとして開始しますが、上にドラッグするとモーダルになります。実際には、ユーザーはまずコンテンツを一目見て、その後Sheetを展開して詳細を確認するか閉じるかを決定できます。
このパターンは特に地図やナビゲーションアプリで人気があります。下部パネルにルートポイントの簡潔な情報が表示され、上にスワイプすると詳細とともに展開されます。Googleマップは、場所やルートに関する情報を表示するために、まさにこの拡張Sheetを使用しています。
| Sheetの種類 | 背景ブロック | 初期状態 | 例 |
|---|---|---|---|
| 標準 | なし | Collapsed | カタログのフィルター |
| モーダル | あり | Expanded | ログインフォーム |
| 拡張 | 部分的 | Collapsed | 場所カード |
Android SDKは、従来のViewベースのアプローチから最新のJetpack Composeまで、Bottom Sheetを実装するための複数のAPIを提供しています。Android用Material Componentsライブラリには、BottomSheetDialogFragmentとBottomSheetBehaviorの既成実装が含まれています。
BottomSheetBehaviorは、任意のViewにアタッチして画面上の位置を制御するMaterial Componentsのクラスです。開発者はSTATE_COLLAPSED、STATE_EXPANDED、STATE_HIDDENの3つの状態を設定します。Behaviorはドラッグジェスチャーとアニメーションを自動的に処理します。
val sheet = findViewById<View>(R.id.bottom_sheet)
val behavior = BottomSheetBehavior.from(sheet)
behavior.state = BottomSheetBehavior.STATE_COLLAPSED
behavior.peekHeight = 200
Jetpack Composeは、Material 3のModalBottomSheetコンポーネントを通じて、より宣言的なAPIを提供します。Viewベースのアプローチとは異なり、ComposeではBottom Sheetは状態とコンテンツを受け取るコンポーザブル関数です。Sheetは、ドラッグ時の表示、非表示、高さ変更を自動的にアニメーション化します。
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FilterSheet(onDismiss: () -> Unit) {
ModalBottomSheet(onDismissRequest = onDismiss) {
Text("フィルター")
Button(onClick = onDismiss) { Text("適用") }
}
}
BottomSheetDialogFragmentは、Bottom Sheetとして表示される特殊なDialogFragmentです。モダリティ、ライフサイクル、構成変更を自動的に処理します。開発者はonCreateViewをオーバーライドしてSheetのレイアウトを返すだけです。Googleのドキュメント(Android Developers、2024)によると、BottomSheetDialogFragmentは従来のAndroidでモーダルBottom Sheetに推奨される方法です。
AppleはiOS 15からUISheetPresentationControllerを通じてシステムレベルのSheetサポートを導入しました。それ以前は、開発者はカスタムトランジションまたはサードパーティライブラリを使用して手動でBottom Sheetを実装していました。AppleのネイティブSheetは、定義済みの展開レベルであるdetentsをサポートしています。
UISheetPresentationControllerは、View ControllerをSheetとして表示する組み込みのプレゼンテーションコントローラです。開発者は、Sheetが停止できる高さの値であるdetentsの配列を指定します。iOS 16では任意の高さのカスタムdetentsが追加されました。Appleは主要なdetentsとしてmedium(.medium)とlarge(.large)を推奨しています。
let viewController = MySheetViewController()
if let sheet = viewController.sheetPresentationController {
sheet.detents = [.medium, .large]
sheet.prefersGrabberVisible = true
sheet.preferredCornerRadius = 16
}
present(viewController, animated: true)
SwiftUIは、Sheetの表示を状態にバインドする.sheet修飾子を提供します。published変数がtrueになると、SwiftUIが自動的にSheetを表示します。SwiftUIはiOS 16で追加された.presentationDetents修飾子を介してdetentsもサポートしています。
struct ContentView: View {
@State private var showSheet = false
var body: some View {
Button("シートを表示") { showSheet = true }
.sheet(isPresented: $showSheet) {
FilterView()
.presentationDetents([.medium, .large])
}
}
}
クロスプラットフォームフレームワークもBottom Sheetをサポートしており、ターゲットプラットフォームに応じて動作を適応させています。FlutterとReact Nativeは、さまざまな柔軟性で組み込みおよびカスタム実装を提供しています。
Flutter SDKには、Bottom Sheetを表示する2つのメソッドがあります。showBottomSheet(標準)とshowModalBottomSheet(モーダル)です。FlutterのMaterial DesignはAndroidと同じ原則に従い、Sheetは下部から表示され、ドラッグをサポートし、高さを調節できます。detentsを使用した拡張動作にはDraggableScrollableSheetが使用されます。
Scaffold.of(context).showBottomSheet((context) {
return Container(
padding: EdgeInsets.all(16),
child: Column(
children: [
Text("シートコンテンツ"),
ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
child: Text("閉じる")
)
]
)
);
})
React Nativeのコアには組み込みのBottom Sheetコンポーネントがないため、開発者はサードパーティライブラリを使用します。最も人気があるのは@gorhom/bottom-sheetです(GitHubで7000以上のスター、2024年)。スナップポイント、アニメーション、固定ヘッダーをサポートするジェスチャ駆動のBottom Sheetを提供します。このライブラリは、60FPSのパフォーマンスを実現するためにReanimated 2とGesture Handler上に構築されています。
Bottom Sheetの適切な使用には、プラットフォームガイドラインの順守と、Sheetが適切なシナリオの理解が必要です。Sheetを設計する際の主要な推奨事項と一般的な間違いを確認しましょう。
Bottom Sheetは、画面上のメインコンテンツに対する補足的なアクションに最適です。フィルター、並べ替え、オプション選択、アイテムの簡単な情報などが典型的なシナリオです。Material Designは、画面全体を占有すべきではないが、Action Sheetやポップアップメニューよりも多くのスペースを必要とするツールにBottom Sheetの使用を推奨しています。
重要な警告やエラーにBottom Sheetを使用しないでください — これにはダイアログウィンドウが適しています。スクロールが必要な多数の入力フィールドがあるSheetは避けてください。ユーザーがキーボードに到達するのに苦労し、コンテキストを失います。Sheetに7つ以上のアクションアイテムが含まれる場合は、Sheetの代わりに別の画面を検討してください。
一般的な間違いは、peekHeight(折りたたまれた状態の高さ)を画面の40%以上に設定することです。ユーザーはSheet全体を見ているのか、それとも展開できるのかがわかりません。Material Designは、コンテンツの顕著な部分を隠したままにしてドラッグを促すために、peekHeightを画面高さの15~30%に設定することを推奨しています。
iOSでは、ユーザーは任意の位置から下にスワイプしてSheetを閉じられることを期待しています。このジェスチャーを無効にすると(UISheetPresentationController.prefersEdgeAttachedInCompactHeight)、ユーザーは慣れた方法でSheetを閉じようとしてフラストレーションを感じます。Appleのヒューマンインターフェースガイドライン(2024)は、スワイプして閉じることはiOSユーザーの基本的な期待であると強調しています。
よくある質問
Bottom Sheetは背景を完全にブロックせず、ドラッグをサポートしますが、ダイアログウィンドウはモーダルで必須のアクションが必要です。Bottom Sheetは補足的なオプションに適しており、ダイアログは重要な確認に適しています。
はい、ただしAndroidマニフェストでadjustResizeまたはadjustPanの設定が必要です。iOSではキーボードが自動的にSheetを持ち上げます。FlutterとReact Nativeでは、正しい配置のためにMediaQueryによる手動処理が必要です。
推奨されるのは、標準Sheetで5~7個以下のアイテムです。それ以上のコンテンツがある場合は、スクロールまたはカテゴリ分割を使用してください。Material Designは、展開状態で画面高さの90%を超えないようにアドバイスしています。
モーダルSheetの場合、AndroidではsetCancelable(true)を、iOSではisModalInPresentation = falseを設定します。Jetpack Composeでは、onDismissRequestが背景タップを自動的に処理します。SwiftUIでは、下へのスワイプがデフォルトの動作です。
はい、すべてのネイティブ実装がVoiceOverとTalkBackをサポートしています。FocusManagerがSheetを開くときに正しくフォーカスをSheetに移動し、閉じるときに戻すことを確認してください。カスタム実装では手動のアクセシビリティ設定が必要です。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。