JUST ENDED
|

iPhone Duo SwiftUI Q&A

Visit the Apple Developer Forums to discuss building SwiftUI layouts that adapt to iPhone Duo. Learn about size classes across both displays, asymmetric safe areas, reserved regions and arrangements, navigation and tab views, and responding to the hinge angle.

Post

Replies

Boosts

Views

Activity

How to implement correct horizontal padding for iPhone Duo
The iPhone Duo outer screen displays a vertical bar on the right edge with view contents inset. A SwiftUI Form displays an appropriate amount of leading padding but 0 padding on the trailing edge, since the vertical bar provides some visual margins already that looks nice. I have a view that looks kind of like a form, multiple stacked text fields, that should align the same way. I used scenePadding to achieve which looks correct on iPhone 18 Pro perfectly aligned with Form, but on iPhone Duo there is extra trailing padding. It doesn't align with my Edit button that remains in the horizontal axis navigation bar (and it is not inset enough on the leading edge, off by a few pixels, interestingly). Note when you unfold it and add the app on the left side in Split View, the vertical bar is on the leading edge, in which case there's too much padding on the leading edge. How can I achieve the correct layout padding/margins? iPhone 18 Pro vs iPhone Duo: My actual app: struct ContentView: View { var body: some View { TabView { NavigationStack { SystemFormView() .navigationTitle("System Form") } .tabItem { Label("System Form", systemImage: "list.bullet.rectangle") } NavigationStack { CustomFormView() .navigationTitle("Custom Form") } .tabItem { Label("Custom Form", systemImage: "rectangle.3.group") } } } } private struct SystemFormView: View { var body: some View { Form { Text("Row 1") Text("Row 2") Text("Row 3") } } } private struct CustomFormView: View { var body: some View { ScrollView { VStack(spacing: 0) { customRow("Row 1") Divider() .padding(.leading) customRow("Row 2") Divider() .padding(.leading) customRow("Row 3") } .background(.background) .scenePadding(.horizontal) } .background(Color(uiColor: .systemGroupedBackground)) } private func customRow(_ title: LocalizedStringKey) -> some View { Text(title) .frame(maxWidth: .infinity, minHeight: 44, alignment: .leading) .padding(.horizontal) } } Note in UIKit, UITableViewController with the inset grouped style has the same layout as Form. A custom view hierarchy can achieve the exact same placement/padding/margins by following these steps: create a scroll view and a content view, set preservesSuperviewLayoutMargins = true on both views, constrain the scroll view to the root view on all edges, constrain the content view to the scrollView.contentLayoutGuide on all edges, constrain the content view width anchor to the scrollView.frameLayoutGuide.widthAnchor, then constrain subviews of the content view to the contentView.layoutMarginsGuide. So I know how to do it in UIKit, how do we in SwiftUI? Thanks!
Topic: UI Frameworks SubTopic: SwiftUI
3
1
148
3h
How to separate/add space between Liquid Glass toolbar items when using ToolbarOverflowMenu
When optimizing for iPhone Duo (and in general) I understand the recommendation is to replace custom More (...) menus with the system overflow menu, otherwise it's possible you can see two (...) buttons or get the custom menu nested inside the system overflow menu. Eek. With the following code, both (+) and (...) are unexpectedly inside one shared Liquid Glass background. How do you separate / add space between them or is this a bug, if so is there a workaround? struct ContentView: View { var body: some View { NavigationStack { Text("Hello, World") .toolbar { ToolbarItem { Button("Add", systemImage: "plus") { } } ToolbarSpacer(.fixed) ToolbarOverflowMenu { Button("Settings", systemImage: "gearshape") { } } } } } } Here's my original code that display two separate buttons as expected: struct ContentView: View { var body: some View { NavigationStack { Text("Hello, World") .toolbar { ToolbarItem { Button("Add", systemImage: "plus") { } } ToolbarSpacer(.fixed) ToolbarItem { Menu { Button("Settings", systemImage: "gearshape") { } } label: { Label("More", systemImage: "ellipsis") } } } } } } Why do I care you might ask? When the user turns on filters in my app, a filter toolbar item is shown, and I want the (+) to remain separate from the grouped filter and more buttons. (+) is like the primary action that should stand alone, like it does in the Wallet app.
Topic: UI Frameworks SubTopic: SwiftUI
3
1
84
4h
About wallpaper app for iPhone Duo
I'm building a wallpaper app for iPhone Duo. The outer display is portrait (1398×2034), and the unfolded inner display is landscape (2670×1878). My wallpapers are portrait images — for example, a person centered in the frame. When the user unfolds the device and the screen changes from portrait to landscape, how does iOS adapt the wallpaper? Specifically: Does it crop a landscape region from the portrait image (which could cut off the subject)? Does it preserve the focal point the user chose when setting the wallpaper, keeping the subject in frame? Or can separate images be assigned to the outer and inner displays? I need to understand this behavior to decide what compositions to offer in my app.
Topic: UI Frameworks SubTopic: SwiftUI
1
0
44
8h
Duo not having different z index with overlay arrangement view style
When using ArrangementView with the overlay style, the Duo simulator does not appear to return a distinct z-index environment value, though it correctly displays z-index 0 and 1 on other iPhone simulators. I would like to use this value to determine whether the overlay is active. Currently, this does not seem possible. Is this behavior intended, or is it a bug? If intended, what is the recommended approach to determine if the Duo is displaying the arranged view over another arranged view? import SwiftUI struct ContentView: View { var body: some View { ArrangementView { Controls() } secondary: { Player() } .arrangementViewStyle(.overlay) } } struct Controls: View { @Environment(\.overlayArrangementZIndex) var zIndex var body: some View { Text("Controls Z:\(zIndex)") .frame(maxWidth: .infinity, alignment: .leading) } } struct Player: View { @Environment(\.overlayArrangementZIndex) var zIndex var body: some View { Text("Player Z:\(zIndex)") .frame(maxWidth: .infinity, alignment: .trailing) } } #Preview { ContentView() }
Topic: UI Frameworks SubTopic: SwiftUI
2
0
60
21h
Handling multi-pass layout updates and debounce strategies during fold/unfold animations
During physical articulation transitions, view bounds, safe areas, and reserved regions frequently resolve across distinct sequential layout passes. In declarative SwiftUI hierarchies, this transient intermediate state can trigger view destruction and recreation if subviews are conditionally bound to active divisions. What is the official guidance for stabilizing view state across these asynchronous layout passes? Should coordinators implement client-side debouncing, or does SwiftUI provide layout primitives to smoothly bridge views across active-to-inactive division transitions without dropping view identity or resetting active tasks?
Topic: UI Frameworks SubTopic: SwiftUI
1
0
33
21h
Best practice for responding to the hinge angle in SwiftUI: Semantic modifiers vs. ReservedRegion queries
Hello! The session description mentions "responding to the hinge angle" in SwiftUI. In the current iOS 27.1 SDK surface, physical division detection relies primarily on inspecting GeometryProxy.reservedRegions(kind: .division). Could you clarify whether Apple recommends modeling posture strictly through geometric division queries, or if there is a recommended/upcoming approach (such as a dedicated view modifier, environment value, or semantic posture state) to observe chassis articulation directly? If continuous or discrete angles are accessible, what is the canonical way to drive declarative layout changes without incurring redundant body re-evaluations during active folding?
Topic: UI Frameworks SubTopic: SwiftUI
1
0
53
22h
Deterministic detection of inner unfolded display vs. outer cover display when flat (180°)
When iPhone Duo is unfolded completely flat to 180°, ReservedRegion(.division) becomes inactive or reports empty geometries. At this point, both the flat inner display and the closed cover display present continuous rectangular surfaces. We currently infer the inner display by evaluating the asymmetric system safe area signature (specifically the presence of the 82 pt top status band versus the trailing lateral cluster on the outer display). Is relying on asymmetric safe area insets the intended pattern to discriminate the active display surface in SwiftUI, or is there a preferred platform API to identify whether a window is hosted on the internal foldable panel versus the cover glass?
Topic: UI Frameworks SubTopic: SwiftUI
1
0
43
22h
Backporting new SwiftUI views
Will the new SwiftUI views added to support the duo (like ArrangementView) be backported to older iOS versions or will we have to provide different implementations if we still want to support iOS 26?
Topic: UI Frameworks SubTopic: SwiftUI
1
0
31
22h
How to implement correct horizontal padding for iPhone Duo
The iPhone Duo outer screen displays a vertical bar on the right edge with view contents inset. A SwiftUI Form displays an appropriate amount of leading padding but 0 padding on the trailing edge, since the vertical bar provides some visual margins already that looks nice. I have a view that looks kind of like a form, multiple stacked text fields, that should align the same way. I used scenePadding to achieve which looks correct on iPhone 18 Pro perfectly aligned with Form, but on iPhone Duo there is extra trailing padding. It doesn't align with my Edit button that remains in the horizontal axis navigation bar (and it is not inset enough on the leading edge, off by a few pixels, interestingly). Note when you unfold it and add the app on the left side in Split View, the vertical bar is on the leading edge, in which case there's too much padding on the leading edge. How can I achieve the correct layout padding/margins? iPhone 18 Pro vs iPhone Duo: My actual app: struct ContentView: View { var body: some View { TabView { NavigationStack { SystemFormView() .navigationTitle("System Form") } .tabItem { Label("System Form", systemImage: "list.bullet.rectangle") } NavigationStack { CustomFormView() .navigationTitle("Custom Form") } .tabItem { Label("Custom Form", systemImage: "rectangle.3.group") } } } } private struct SystemFormView: View { var body: some View { Form { Text("Row 1") Text("Row 2") Text("Row 3") } } } private struct CustomFormView: View { var body: some View { ScrollView { VStack(spacing: 0) { customRow("Row 1") Divider() .padding(.leading) customRow("Row 2") Divider() .padding(.leading) customRow("Row 3") } .background(.background) .scenePadding(.horizontal) } .background(Color(uiColor: .systemGroupedBackground)) } private func customRow(_ title: LocalizedStringKey) -> some View { Text(title) .frame(maxWidth: .infinity, minHeight: 44, alignment: .leading) .padding(.horizontal) } } Note in UIKit, UITableViewController with the inset grouped style has the same layout as Form. A custom view hierarchy can achieve the exact same placement/padding/margins by following these steps: create a scroll view and a content view, set preservesSuperviewLayoutMargins = true on both views, constrain the scroll view to the root view on all edges, constrain the content view to the scrollView.contentLayoutGuide on all edges, constrain the content view width anchor to the scrollView.frameLayoutGuide.widthAnchor, then constrain subviews of the content view to the contentView.layoutMarginsGuide. So I know how to do it in UIKit, how do we in SwiftUI? Thanks!
Topic: UI Frameworks SubTopic: SwiftUI
Replies
3
Boosts
1
Views
148
Activity
3h
How to separate/add space between Liquid Glass toolbar items when using ToolbarOverflowMenu
When optimizing for iPhone Duo (and in general) I understand the recommendation is to replace custom More (...) menus with the system overflow menu, otherwise it's possible you can see two (...) buttons or get the custom menu nested inside the system overflow menu. Eek. With the following code, both (+) and (...) are unexpectedly inside one shared Liquid Glass background. How do you separate / add space between them or is this a bug, if so is there a workaround? struct ContentView: View { var body: some View { NavigationStack { Text("Hello, World") .toolbar { ToolbarItem { Button("Add", systemImage: "plus") { } } ToolbarSpacer(.fixed) ToolbarOverflowMenu { Button("Settings", systemImage: "gearshape") { } } } } } } Here's my original code that display two separate buttons as expected: struct ContentView: View { var body: some View { NavigationStack { Text("Hello, World") .toolbar { ToolbarItem { Button("Add", systemImage: "plus") { } } ToolbarSpacer(.fixed) ToolbarItem { Menu { Button("Settings", systemImage: "gearshape") { } } label: { Label("More", systemImage: "ellipsis") } } } } } } Why do I care you might ask? When the user turns on filters in my app, a filter toolbar item is shown, and I want the (+) to remain separate from the grouped filter and more buttons. (+) is like the primary action that should stand alone, like it does in the Wallet app.
Topic: UI Frameworks SubTopic: SwiftUI
Replies
3
Boosts
1
Views
84
Activity
4h
About wallpaper app for iPhone Duo
I'm building a wallpaper app for iPhone Duo. The outer display is portrait (1398×2034), and the unfolded inner display is landscape (2670×1878). My wallpapers are portrait images — for example, a person centered in the frame. When the user unfolds the device and the screen changes from portrait to landscape, how does iOS adapt the wallpaper? Specifically: Does it crop a landscape region from the portrait image (which could cut off the subject)? Does it preserve the focal point the user chose when setting the wallpaper, keeping the subject in frame? Or can separate images be assigned to the outer and inner displays? I need to understand this behavior to decide what compositions to offer in my app.
Topic: UI Frameworks SubTopic: SwiftUI
Replies
1
Boosts
0
Views
44
Activity
8h
Is the hinge area always un-tappable, or only when the device is folded
I noticed the hinge keep out has an isActive status on it. does that mean the hinge area is only un-tappable when it is active? As in, when the device is open 100%, the hinge area can receive taps? Same for the camera cutout on the inside screen, I assume it's only active when the camera is being used?
Topic: UI Frameworks SubTopic: SwiftUI
Replies
0
Boosts
0
Views
24
Activity
20h
Duo not having different z index with overlay arrangement view style
When using ArrangementView with the overlay style, the Duo simulator does not appear to return a distinct z-index environment value, though it correctly displays z-index 0 and 1 on other iPhone simulators. I would like to use this value to determine whether the overlay is active. Currently, this does not seem possible. Is this behavior intended, or is it a bug? If intended, what is the recommended approach to determine if the Duo is displaying the arranged view over another arranged view? import SwiftUI struct ContentView: View { var body: some View { ArrangementView { Controls() } secondary: { Player() } .arrangementViewStyle(.overlay) } } struct Controls: View { @Environment(\.overlayArrangementZIndex) var zIndex var body: some View { Text("Controls Z:\(zIndex)") .frame(maxWidth: .infinity, alignment: .leading) } } struct Player: View { @Environment(\.overlayArrangementZIndex) var zIndex var body: some View { Text("Player Z:\(zIndex)") .frame(maxWidth: .infinity, alignment: .trailing) } } #Preview { ContentView() }
Topic: UI Frameworks SubTopic: SwiftUI
Replies
2
Boosts
0
Views
60
Activity
21h
Handling multi-pass layout updates and debounce strategies during fold/unfold animations
During physical articulation transitions, view bounds, safe areas, and reserved regions frequently resolve across distinct sequential layout passes. In declarative SwiftUI hierarchies, this transient intermediate state can trigger view destruction and recreation if subviews are conditionally bound to active divisions. What is the official guidance for stabilizing view state across these asynchronous layout passes? Should coordinators implement client-side debouncing, or does SwiftUI provide layout primitives to smoothly bridge views across active-to-inactive division transitions without dropping view identity or resetting active tasks?
Topic: UI Frameworks SubTopic: SwiftUI
Replies
1
Boosts
0
Views
33
Activity
21h
Best practice for responding to the hinge angle in SwiftUI: Semantic modifiers vs. ReservedRegion queries
Hello! The session description mentions "responding to the hinge angle" in SwiftUI. In the current iOS 27.1 SDK surface, physical division detection relies primarily on inspecting GeometryProxy.reservedRegions(kind: .division). Could you clarify whether Apple recommends modeling posture strictly through geometric division queries, or if there is a recommended/upcoming approach (such as a dedicated view modifier, environment value, or semantic posture state) to observe chassis articulation directly? If continuous or discrete angles are accessible, what is the canonical way to drive declarative layout changes without incurring redundant body re-evaluations during active folding?
Topic: UI Frameworks SubTopic: SwiftUI
Replies
1
Boosts
0
Views
53
Activity
22h
Deterministic detection of inner unfolded display vs. outer cover display when flat (180°)
When iPhone Duo is unfolded completely flat to 180°, ReservedRegion(.division) becomes inactive or reports empty geometries. At this point, both the flat inner display and the closed cover display present continuous rectangular surfaces. We currently infer the inner display by evaluating the asymmetric system safe area signature (specifically the presence of the 82 pt top status band versus the trailing lateral cluster on the outer display). Is relying on asymmetric safe area insets the intended pattern to discriminate the active display surface in SwiftUI, or is there a preferred platform API to identify whether a window is hosted on the internal foldable panel versus the cover glass?
Topic: UI Frameworks SubTopic: SwiftUI
Replies
1
Boosts
0
Views
43
Activity
22h
Backporting new SwiftUI views
Will the new SwiftUI views added to support the duo (like ArrangementView) be backported to older iOS versions or will we have to provide different implementations if we still want to support iOS 26?
Topic: UI Frameworks SubTopic: SwiftUI
Replies
1
Boosts
0
Views
31
Activity
22h
UIUserInterfaceIdiom for Duo is still ".phone" ?
We are wondering if there would be a new enum case for UIUserInterfaceIdiom for iPhone Duo or we should use the case .phone
Topic: UI Frameworks SubTopic: SwiftUI
Replies
1
Boosts
1
Views
62
Activity
22h
Horizontal Scrolling Beyond Vertical Safe Area
Is it acceptable to have horizontal scrolling below tab view/ toolbar(Vertical Safe Area) on iPhone Duo or should I clipped it? Thank you
Topic: UI Frameworks SubTopic: SwiftUI
Replies
0
Boosts
1
Views
42
Activity
22h