iPhone Duoの折り目やカメラの位置を取得する

iPhone Duoの折り目やカメラの位置を取得する

iOS 27.1で追加されたiPhone Duo向けAPIの`reservedRegions`について、折り目やカメラが占める領域の取得方法と使い方を詳しく解説します。
2026.11.04

はじめに

以前、iOS 27.1 SDKで追加されたiPhone Duo向けのAPIをまとめました。

https://dev.classmethod.jp/articles/ios27-1-iphone-duo-new-apis/

今回はその中からreservedRegionsを深掘りします。

検証環境

  • Xcode 27.2
  • iOS 27.2 iPhone Duo シミュレーター

reservedRegionsとは

GeometryProxyのreservedRegionsで、折り目やカメラなど、画面を遮るものが占める領域を取得できる。

func reservedRegions(
    kind: ReservedRegion.Kind,
    options: ReservedRegion.QueryOptions = [],
    layoutDirectionBehavior: LayoutDirectionBehavior = .mirrors
) -> [ReservedRegion]

返ってくるReservedRegionのframeは、取得したViewの座標。margins(周りの余白)も最初から含まれている。

https://developer.apple.com/documentation/swiftui/geometryproxy/reservedregions(kind:options:layoutdirectionbehavior:)

kind

型はReservedRegion.Kind
取得したい領域の種類を指定する。値は2つ。

  • .occlusion: カメラやDynamic Islandなど、何かに覆われる場所
  • .division: 折り目。半開きのときだけ有効で、全開のときは無効になり、デフォルトでは返ってこない。ドキュメントでは閉じているときも有効とされているが、シミュレーターでは閉じると.includeInactive(後述)を付けても返ってこなかった

iPhone Duoの場合、外側のカメラは常に有効、内側のカメラは使っている間だけ有効になる。

取得できた領域に色をつけて可視化してみた。

  • 赤:.occlusion
  • 青:.division
全開 半開

options

型はReservedRegion.QueryOptions
指定できるのは.includeInactiveのみ。
OptionSetなので、単体でも[.includeInactive]のように配列リテラルでも指定できる。推測になるが、今後オプションが増える可能性を考慮していると思う。

デフォルト([])では有効な領域だけが返ってくるが、.includeInactiveを渡すと無効な領域も返ってくる。有効かどうかはisActiveで判定できる。

内側のカメラや折り目のように、状態によって有効になる領域にあらかじめスペースを空けておけば、レイアウトがガタつかない。

.includeInactiveを渡して領域を取得し表示してみた。
指定なしの状態に比べ、有効でない領域も取得できていることが分かる。

  • 赤:.occlusion
  • 青:.division
全開 半開

layoutDirectionBehavior

アラビア語など右から左に書く言語(RTL)では、SwiftUIは座標を右端から測る。layoutDirectionBehaviorは、frameの数字をどちらの端から測って返すかを決める。領域の場所そのものは変わらない。

  • .mirrors(デフォルト): RTLのときは右端から測る(SwiftUIのレイアウトと同じ)
  • .fixed: 反転しない(RTLでもLTRと同じく左端から測る)

iPhone Duoのシミュレーターで、右端にあるカメラ(.occlusion)のframeを取ってみた。RTLは.environment(\.layoutDirection, .rightToLeft)で再現している。画面の幅は951。

.mirrors .fixed
LTR 867.0〜951.0 867.0〜951.0
RTL 0.0〜84.0 867.0〜951.0

RTLの.mirrorsは右端から測った値になる。SwiftUIもRTLでは右端から測って配置するので、この値をそのまま使えばカメラの位置に揃う。普通はデフォルトのままでいい。

どちらで測るかは、端末の言語ではなく、取得したViewのlayoutDirectionで決まる。.environment(\.layoutDirection, .leftToRight)を付けたViewでは、.mirrorsも左端から測った値になった。

.fixedは、レイアウト方向に合わせて反転しない配置をするときに使う(公式ドキュメントより)。ただ、普通のSwiftUIのレイアウトはRTLで自動的に反転するので、.fixedが必要になる場面は自分ではよくわからなかった。

使ってみる

.divisionのframeの縦横を比べると、折り目の向きがわかる。これを使って2つのViewを折り目の左右(上下)に分けてみた。

FoldAwareSplit.swift
GeometryReader { proxy in
    if let fold = proxy.reservedRegions(kind: .division).first?.frame {
        if fold.height > fold.width { // 縦の折り目
            HStack(spacing: fold.width) {
                LeftView().frame(width: fold.minX)
                RightView().frame(width: proxy.size.width - fold.maxX)
            }
        } else { // 横の折り目
            VStack(spacing: fold.height) {
                TopView().frame(height: fold.minY)
                BottomView().frame(height: proxy.size.height - fold.maxY)
            }
        }
    } else { // 折り目がない(全開)
        SingleView()
    }
}
本のように半開 ノートPCのように半開 全開

ただ、2つのViewを折り目の両側に分けたいだけなら、自分で領域を読まずにArrangementViewを使うよう公式ドキュメントで推奨されている。

まとめ

  • frameは取得したViewの座標
  • .divisionは半開きのときだけ有効。全開のときは.includeInactiveで無効として取れる
  • layoutDirectionBehaviorはRTLのときのframeの測り方を決める。普通はデフォルトのままでいい

ドキュメントではシステム提供のViewが調整してくれるとされているので、出番は限られそう。

参考

https://dev.classmethod.jp/articles/ios27-1-iphone-duo-new-apis/
https://developer.apple.com/documentation/swiftui/geometryproxy/reservedregions(kind:options:layoutdirectionbehavior:)
https://developer.apple.com/documentation/swiftui/reservedregion
https://developer.apple.com/documentation/swiftui/reservedregion/kind-swift.struct/division
https://developer.apple.com/documentation/swiftui/layoutdirectionbehavior
https://developer.apple.com/documentation/swiftui/arrangementview

この記事をシェアする

関連記事