
iPhone Duo向けにiOS 27.1で追加されたAPIは何ができるのか
はじめに
iOS 27.1 SDKで折りたたみ端末iPhoneDuo向けのAPIが新しく追加されました。
大きく分けると以下のような感じ
- 2つのViewを並べる・重ねる(
ArrangementView) - ヒンジの開き具合を知る(
onHingeChange) - 折り目やカメラの位置を知る(
reservedRegions) - 縦に並ぶツールバー等を調整する(
toolbarVerticalBehaviorなど)
どれもSwiftUI/UIKitの両方に用意されており、少し調べてみたのでまとめます。
細かい挙動などは別途個別に記事を書こうと思います。
2つのViewを並べる/重ねる
ArrangementView
ArrangementViewは、primaryとsecondaryの2つのViewを、表示サイズや環境に合わせて並べたり重ねたりすることができるコンテナ。
ArrangementView {
PrimaryView()
} secondary: {
SecondaryView()
}
.arrangementViewStyle(.split) // あるいは(.overlay)
スタイルは .automatic/.split/.overlayの3つ。デフォルトの.automaticは .splitとして解決されるとドキュメントに記載ありなので実質2つ。
.splitは2つの画面を並べるスタイル。例えば左側で音楽再生中の画面/右側で歌詞表示画面のように、どちらも隠したくない組み合わせ向き。
.overlayはprimaryをsecondaryの上に重ねるスタイル。あんまり使い道が思い浮かばなかった。ドキュメントに最適なユースケースの記載はあったが正直ピンときてない。
以下ドキュメントの抜粋
An overlay arrangement layers the primary view on top of the secondary view in z-order. This layout is well-suited for full screen experiences like media players, where playback controls overlay a video surface:
オーバーレイ配置では、Z順に従ってプライマリビューがセカンダリビューの上に重ねられます。このレイアウトは、メディアプレーヤーのように、再生コントロールが動画画面の上に重なるようなフルスクリーン表示に最適です
ヒンジの開き具合を知る
onHingeChange(isEnabled:_:)
onHingeChangeで、ヒンジの状態と角度を受け取れる。クロージャには変化前と変化後の値が渡されます。
.onHingeChange { _, newContext in
guard let hinge = newContext.hinge else { return }
let degrees = hinge.angle.degrees // 角度 Angle型なのでdegreesで度数を取り出す
let status = hinge.status // .closed / .partiallyOpen / .fullyOpen
}
hinge.statusは.closed/.partiallyOpen/.fullyOpenの3つで、角度は連続して更新される。
ユースケースは限定的だと思いますが、単純にイベントを取れるように用意してくれるのはありがたい。
折り目や画面を遮るものの位置を知る
reservedRegions(kind:options:layoutDirectionBehavior:)
GeometryProxyのreservedRegionsで、折り目や、カメラなど画面を遮るものが占める領域を取得できます。
GeometryReader { proxy in
// 折り目の領域
let folds = proxy.reservedRegions(kind: .division)
// 画面を遮るもの(カメラなど)の領域
let occlusions = proxy.reservedRegions(kind: .occlusion)
}
領域の種類は2つ。
.division: 折り目のように、領域を2つに分ける場所。折りたたんでいるときだけ有効になる.occlusion: 要素に遮られる場所。公式ドキュメントでは、Dynamic Island、カメラ、ウインドウのコントロールが例に挙がっている。
optionsや、layoutDirectionBehaviorの引数は別途深掘り記事で書くことにする
縦に並ぶシステムバーを調整する
toolbarVerticalBehavior(_:)
iPhone Duoでは、ナビゲーションバー・ツールバー・タブバーが画面の横に縦並びで表示される。
縦並びになるのは、外側のディスプレイと、内側のディスプレイを横向きで使う時。内側のディスプレイを縦向きで使うときは、従来の横向きのバーに戻る。
toolbarVerticalBehavior(_:)は、縦並びのバーを使うかどうかを決める。値は2つ。
.automatic: 縦並びのバーを使うかをシステムが判断.disabled: 縦並びを使わず、横並びのバーで表示
TabView {
Tab("ホーム", systemImage: "house") {
NavigationStack { HomeView() }
}
Tab("設定", systemImage: "gear") {
NavigationStack { SettingsView() }
}
}
.toolbarVerticalBehavior(.disabled) // 縦並びのバーを無効化
toolbarVerticalCompressionBehavior(_:)
toolbarVerticalCompressionBehavior(_:)は、縦並びになったタブバーやツールバー項目が縦の長さから溢れた場合に、どちらを優先して残すかを決めることができる。優先されなかった方は、3点のボタンで省略される。値は3つ。
.automatic: システムに任せる.prefersToolbarItems: ツールバー項目を優先して残す.prefersTabBar: タブバーを優先して残す
TabView {
Tab("最近使った項目", systemImage: "clock") {
NavigationStack {
RootView()
.toolbar {
Button("上へ", systemImage: "chevron.up") {}
Button("下へ", systemImage: "chevron.down") {}
}
.toolbarVerticalCompressionBehavior(.prefersToolbarItems) // 溢れた場合Toolbarを残す
}
}
}
axisBehavior(_:)
axisBehavior(_:) は、ToolbarItemなどのツールバー項目ごとに、縦と横のどちらのバーに表示するかを決められる。値は3つ。
.automatic: システムに任せる.horizontalOnly: 横のバーに表示される.verticalPreferred: 縦と横のどちらにも表示できるが、両方のバーがあるときは縦を優先する
.toolbar {
ToolbarItem(placement: .primaryAction) {
Toggle(isOn: $isOn) { ... }
}
.axisBehavior(.horizontalOnly) // 横のバーに表示される
}
toolbarVerticalEdge
toolbarVerticalEdgeは、縦並びのバーが置かれる場所を読み取れるEnvironment。型はHorizontalEdge?。
- 値は、縦並びのバーが今表示されているかに関係なく、システムが優先する場所(
.leading/.trailing)が取れる - 縦並びバーを使わない端末・サイズクラス・向きではnilになる
公式ドキュメントでは、独自のパレットを縦並びのバーと同じ側に寄せるユースケースが紹介されている。
struct ContentView: View {
@Environment(\.toolbarVerticalEdge) var toolbarVerticalEdge
var body: some View {
FloatingToolPalette()
.frame(maxWidth: .infinity,
alignment: toolbarVerticalEdge == .trailing
? .trailing : .leading) // バーがtrailing側ならtrailingに、leading側かnilならleadingに寄せる
}
}
ナビゲーションバー・ツールバー・タブバー周りの制御が複雑になった...システム準拠の挙動がどうなるかなど検証が必要だなと感じた。
参考










