開発者向け
iPhone Duo の予約領域:UI を折り目から遠ざける方法
SwiftUI で iPhone Duo の予約領域から折り目とカメラの位置を取得する方法。reservedRegions(kind:)、.division と .occlusion、.includeInactive、frame を解説。
AppleDuo 編集部公開 13分で読めます
このページの内容
iPhone Duo を部分的に折りたたむと、内側ディスプレイの中央が曲がります。折り目をまたぐテキストは読みにくく、そこにかかったボタンはタップしにくくなります。外側と内側のカメラもスペースを占めます。iPhone Duo の予約領域は、こうした領域がどこにあるかをシステムがアプリに伝える仕組みで、これを使えば自分の UI をそこから遠ざけられます。このガイドでは、SwiftUI の API、折り目が有効と見なされるタイミング、領域に含まれる情報、そしてテーブルトップモードで遊ぶゲームにとってこれらが何を意味するかを解説します。
予約領域は iOS 27.1 の新機能で、SwiftUI と UIKit で利用できます。Apple の講演「Prepare your app for iPhone Duo」では、iPhone Duo のフルスクリーン体験には Xcode 27.1 が必要だとされています。これらは新しい API なので、リリース間で詳細が変わる可能性があります。この記事は、Apple のドキュメント、Tech Talk「Strike a pose with adaptive layouts on iPhone Duo」、そして iOS 開発者 Sarun Wongpatcharapakorn(@sarunw)の投稿をもとに書いています。私たちはこれらの値を実機で計測してはいません。
iPhone Duo の予約領域とは?
予約領域(reserved region)とは、ビューの中で何かほかのもの、たいていはデバイスのハードウェアが確保している領域です。Sarun はこれを簡潔に、予約領域は UI を置くべきでない場所を教えてくれるもの、と説明しています。
Tech Talk で Apple は、iPhone Duo の予約領域として、外側カメラ、有効になっている間の内側カメラ、折り目の3つを挙げています。種類は2つです。
| 種類 | 意味 | iPhone Duo での例 | 対応方法 |
|---|---|---|---|
.occlusion | 何かがビューの一部を覆っている | 外側カメラ、内側カメラ、Dynamic Island | コンテンツをその領域の外へ移動する |
.division | 何かがビューを複数の部分に分けている | 折り目 | コンテンツをその両側に配置する |
折り目は1つの大きな領域を2つの小さな領域に分けるので、division に分類されます。そこでコンテンツが隠れるわけではありませんが、折り目をまたぐものはすべて使いにくくなります。
ほとんどのアプリでは、これらの領域を読み取る必要はありません。Apple のドキュメントによると、split view、タブバー、アラート、コンテキストメニュー、シートなどのフレームワークのビューは、すでに折り目とカメラに合わせて調整されます。この API が必要なのはカスタム UI の場合です。フローティングコントロール、カスタムのオーバーレイ、ゲームの HUD、そして明示的なフレームやカスタムの Layout で配置するものすべてが該当します。開発者フォーラムでも Apple のエンジニアが同じアドバイスをしています。組み込みのコンテナを優先し、カスタムの2ビューレイアウトには ArrangementView を使い、手動でレイアウトするコントロールには予約領域を使う、というものです。
SwiftUI で予約領域を読み取るには?
コンテンツを GeometryReader で囲み、proxy で reservedRegions(kind:) を呼び出します。Sarun が示している呼び出しは次のとおりです。
GeometryReader { proxy in
let regions = proxy.reservedRegions(kind: .division)
}
Apple のドキュメントページに載っている完全なシグネチャは次のとおりです。
func reservedRegions(
kind: ReservedRegion.Kind,
options: ReservedRegion.QueryOptions = [],
layoutDirectionBehavior: LayoutDirectionBehavior = .mirrors
) -> [ReservedRegion]
このメソッドはビューと重なる領域だけを返し、frame はビューの座標空間で表されます。領域がビューと部分的にしか重ならない場合、frame が負の座標から始まったり、ビューのサイズを超えて広がったりすることがあります。
Tech Talk では、geometry proxy を取得する別の方法として onGeometryChange も紹介されています。ReservedRegion は Equatable なので、領域を監視して state に保存できます。以下は Apple のサンプルコードではなく、私たちが書いたこのパターンのスケッチです。
struct GameBoard: View {
@State private var folds: [ReservedRegion] = []
var body: some View {
BoardView(folds: folds)
.onGeometryChange(for: [ReservedRegion].self) { proxy in
proxy.reservedRegions(kind: .division)
} action: { newValue in
folds = newValue
}
}
}
UIKit では、UIView で reservedRegions(kind:) を呼び出し、各領域の frame を読み取ります。
let regions = view.reservedRegions(kind: .division)
let frames = regions.map(\.frame)
ヒンジが占めるスペースではなく、ヒンジの角度そのものに反応したい場合は、iPhone Duo のヒンジの状態と onHingeChange を参照してください。
折り目の division 領域が有効になるのはいつ?
折り目が常に問題になるわけではありません。iPhone Duo をフラットに開いているときはディスプレイがひと続きなので、コンテンツが中央をまたいでもかまいません。そのため、division 領域は姿勢に応じてオンとオフが切り替わります。
| 姿勢 | 折り目の division 領域 | デフォルトで返される? |
|---|---|---|
| 完全に開いた状態、フラット | 無効 | いいえ |
| 部分的に開いた状態、本のような形 | 有効、縦方向に走る | はい |
| 部分的に開いた状態、ノートパソコンのような形 | 有効、横方向に走る | はい |
Sarun によると、reservedRegions はデフォルトで有効な領域だけを返し、折り目の division はデバイスが折りたたまれているときだけ有効になります。Apple の ReservedRegion のドキュメントにも同じことが書かれており、iPhone Duo が完全に開いているとき、このメソッドは division 領域を返しません。内側カメラも同様で、カメラの使用中だけ有効になる occlusion 領域です。外側カメラは常に有効です。
無効な領域も取得するには、.includeInactive を渡します。
GeometryReader { proxy in
let regions = proxy.reservedRegions(
kind: .division,
options: .includeInactive)
let foldIsActive = regions.contains { $0.isActive }
Text(foldIsActive ? "Folded" : "Flat")
}
なぜ無効な領域が必要になるのでしょうか。Apple は2つの理由を挙げています。無効な領域にも frame があるので、あらかじめそのスペースを空けておけば、ユーザーがデバイスを折りたたんだときにレイアウトが急に動くのを防げます。また、division が存在する場合は常にグリッドの列数を偶数にする、といった大まかな判断にも使えます。Tech Talk では、デバイスがフラットなときは折り目の領域の幅がゼロになると補足されているので、無効な領域は正確な余白の計算ではなく、こうした判断に使ってください。
ReservedRegion には何が含まれている?
各領域は5つのプロパティを持つ小さな値です。いずれも iOS 27.1 の Apple のドキュメントに記載されています。
| プロパティ | 型 | わかること |
|---|---|---|
frame | CGRect | 領域の位置。ビューの座標系でポイント単位、margins を含む |
margins | EdgeInsets | インタラクティブなコンテンツを遠ざけるための、予約エリア周囲の追加スペース |
isActive | Bool | その領域が現在ビューに影響しているかどうか |
kind | ReservedRegion.Kind | .occlusion または .division |
id | ReservedRegion.ID | クエリをまたいで変わらない識別子 |
見落としやすい点が3つあります。1つ目は、(0, 0) は画面ではなく、領域を読み取ったビューの左上(top-leading)の角だということです。座標が一致するよう、コンテンツを配置するのと同じビューで領域を読み取ってください。カスタムの Layout では、計算はゼロから行い、各サブビューを配置するときにだけ bounds.origin を加えます。
2つ目は、frame にはすでに margins が含まれているので、もう一度加えないことです。3つ目は、frame はデフォルトで右から左に書く言語向けに反転されることです。コンテンツがレイアウト方向に合わせて反転しない場合は、layoutDirectionBehavior: .fixed を渡して反転されていない frame を取得してください。
規模感をつかむために紹介すると、Sarun による外側ディスプレイのデモでは、有効な occlusion 領域が2つ表示されています。1つは (399, 29) にある 37 × 37 ポイント、もう1つは (382, 0) にある 84 × 170 ポイントの領域です。これらは右上(top trailing)の角にあるカメラとその横のステータス領域を覆っています。これは iOS 27.x での一例として扱い、定数とは考えないでください。frame はビューからの相対値で、リリース間で変わる可能性があるため、実行時に読み取るようにします。同じ問題のセーフエリア側については、iPhone Duo のセーフエリアとコンテンツマージンを参照してください。
ボタンやテキストを折り目から遠ざけるには?
division が有効なときは、各ビューを完全にその片側に収めます。frame の幅と高さを比べれば折り目の向きがわかるので、それに合わせてペインのサイズを決めます。以下のビューは Apple のドキュメントをもとにしたものです。
struct SplitContent: View {
var body: some View {
GeometryReader { proxy in
if let fold = proxy.reservedRegions(kind: .division).first,
fold.frame.width > fold.frame.height {
// ノートパソコンのように折りたたまれている:ペインを上下に積む。
VStack(spacing: 0) {
ScreenView()
.frame(height: fold.frame.minY)
Color.clear
.frame(height: fold.frame.height)
ControlsView()
.frame(height: proxy.size.height - fold.frame.maxY)
}
} else {
// フラット、または本のように折りたたまれている:1つのレイアウト。
SingleLayout()
}
}
}
}
透明なスペーサーは division とちょうど同じサイズなので、折り目には何もかかりません。Apple の完全な例では、本のような縦方向の折り目も HStack で処理しています。iPhone Duo の折り目は1つなので、.first を読み取れば十分です。
Apple のガイダンスにあるいくつかのルールが役立ちます。
- 必要なものだけを動かす。 大きく移動したり消えたりするコントロールは、再び見つけにくくなります。小さな調整を心がけ、関連するコントロールはまとめて動かしてください。
- スクロールするコンテンツはスクロールに任せる。 list や scroll view はデフォルトでは折り目に合わせて行を分割しません。Tech Talk によると、スクロールするコンテンツは一般に自動的に適応します。行が読みにくくなったり使いにくくなったりした場合にだけ division を確認してください。
- 列数を偶数にする。 グリッドでは、偶数の列数にすると折り目できれいに分かれます。
- コンテナを優先する。 画面が関連する2つのビューで構成されているなら、SwiftUI の ArrangementView を使えば、自分で領域を読み取らなくても2つを折り目の反対側に保てます。
予約領域はゲームデザインにどう関係する?
ゲームは独自の UI を描画することが多いため、システムが代わりに動かしてくれることはありません。折り目をまたぐ体力バー、発射ボタン、スコアは、まさに Apple が確認するよう求めているカスタムコンテンツです。
テーブルトップの姿勢、つまりノートパソコンのように折りたたんでテーブルに置いた状態では、レイアウトの考え方が変わります。デザイナーの Blake Crosley がまとめた Apple のデザインガイダンスによると、上側の領域は離れて見るコンテンツに適し、下側の領域はインタラクティブなコントロールに適しています。Apple のガイドラインはさらに、姿勢が変わってもゲームが画面を埋めること、レターボックスよりもアスペクト比の変更を優先すること、リサイズ時にテキストとコントロールのサイズを一定に保つことを求めています。
AppleDuo に掲載しているゲームのいくつかは、この分割を中心に作られています。Battlefleet Duo では、自分の艦隊が下の画面にあり、敵の海域が立てた画面に表示され、2人のプレイヤーが折り目を挟んで向かい合えます。Greens: Mini-golf はノートパソコンのように折りたたみ、下半分でパットを打つと、立てた上半分がキャディカメラになります。これらのゲームの説明はストアの掲載情報に基づくもので、内部でどの API を使っているかはわかりません。
自分のゲームについては、API から次のようなパターンが導けます。
- 折り目を HUD の継ぎ目として扱う。 division の frame を境界として、タッチ操作は平らな側に、ステータスは立てた側に固定します。
- プレイフィールドの重要な部分を空けておく。 背景アートが折り目をまたぐのはかまいませんが、ユーザーがタップしなければならない標的、カード、タイルは避けてください。
- 両方の向きを想定する。 本のような折り目は縦方向に走るので、2人のプレイヤーや2つの盤面を左右に並べる分割に向いています。ノートパソコンのような折り目は横方向に走ります。
- レイアウトが急に動くのを避ける。 突然のずれがプレイを妨げるようなら、
.includeInactiveでクエリして、早めにスペースを確保しておきます。
ほかの例は Built for the Fold ページで紹介しています。
まとめ:押さえておくべきポイントは?
- iOS 27.1 で追加された予約領域は、UI が使うべきでない領域を示します。iPhone Duo では、外側カメラ、有効になっている間の内側カメラ、折り目が該当します。
- 折り目には
proxy.reservedRegions(kind: .division)、カメラには.occlusionを使います。UIKit のUIViewにも同じメソッドがあります。 - 折り目の division は、iPhone Duo が部分的に折りたたまれているときだけ有効です。フラットなときにも取得するには
.includeInactiveを渡し、isActiveを確認します。 - 各
ReservedRegionは、ビューの座標系でポイント単位のframeを持ち、marginsはすでに含まれています。コンテンツを配置するビューで読み取ってください。 - 標準のコンテナはすでに折り目に対応しています。予約領域はカスタムのコントロール、オーバーレイ、ゲームの HUD に使い、タップできるものはすべて折り目の片側に収めてください。
次は、ArrangementView が2ビューレイアウトで折り目をどう扱うかを確認するか、iPhone Duo 対応準備チェックリストを進めてください。
よくある質問
iPhone Duo の予約領域とは何ですか?
予約領域とは、ハードウェアが確保しているディスプレイ上の領域で、コンテンツはそこを避ける必要があります。iPhone Duo には、外側カメラ、使用中の内側カメラ、折り目の3つがあります。
SwiftUI で iPhone Duo の折り目を取得するには?
コンテンツを GeometryReader で囲み、proxy.reservedRegions(kind: .division) を呼び出します。折り目は ReservedRegion として返され、その frame はビューの座標空間でポイント単位で表され、margins もすでに含まれています。
フラットな iPhone Duo で reservedRegions が空の配列を返すのはなぜですか?
このメソッドはデフォルトで有効な領域だけを返し、iPhone Duo が完全に開いているときは折り目の division 領域が無効になるためです。それでも取得したい場合は options: .includeInactive を渡し、各領域の isActive を確認してください。
division 領域と occlusion 領域の違いは何ですか?
occlusion 領域は、カメラや Dynamic Island のようにビューの一部を覆うものなので、コンテンツをそこから移動させます。division 領域は、折り目のようにビューを複数の部分に分けるものなので、コンテンツをその両側に配置します。
標準の SwiftUI ビューを使っていても予約領域は必要ですか?
通常は必要ありません。Apple によると、split view、タブバー、アラート、コンテキストメニュー、シートはすでに折り目とカメラに合わせて調整されます。予約領域を確認すべきなのは、カスタムのオーバーレイ、フローティングコントロール、明示的なフレームやカスタムの Layout で配置するビューです。
出典
- Strike a pose with adaptive layouts on iPhone Duo (Tech Talk) · Apple Developer
- ReservedRegion · Apple Developer Documentation
- reservedRegions(kind:options:layoutDirectionBehavior:) · Apple Developer Documentation
- Prepare your app for iPhone Duo (Tech Talk) · Apple Developer
- Designing for iPhone Duo · Apple Human Interface Guidelines
- How to know when active regions are changing? · Apple Developer Forums
- Designing for iPhone Duo · Blake Crosley
- Reserved regions tell you where you shouldn't put your UI · @sarunw on X
- reservedRegions returns only active regions by default; pass .includeInactive · @sarunw on X
- Position and size of ReservedRegion on iPhone Duo · @sarunw on X
折りたたみを、アプリの一部に。
AppleDuo は iPhone Duo を本当に活かしたアプリとゲームを紹介しています。App Store へのリンクを添えて送ってください。ほかの開発者の作品も見られます。