WWDC.ai

Raise the bar with iPhone Duo

Adapt SwiftUI and UIKit navigation, toolbar, and tab bar layouts for iPhone Duo's vertical shared bar region, overflow, and custom item behavior.

Watch on Apple Developer

TL;DR

  • Rebuild with the latest SDKs and use system navigation containers so navigation, toolbar, and tab bar items can participate in iPhone Duo's vertical bar layout.
  • Vertical bars use a shared side region with a top-to-bottom hierarchy; keep items associated with their containers and use the new placements for primary navigation and pinned actions.
  • Toolbar content should favor symbol-only representations, complete item metadata, and explicit axis behavior for custom or transitional controls.
  • Plan for overflow on constrained displays by configuring compression behavior, consolidating custom overflow into the system menu, and assigning visibility priorities.

Opt in by using system-managed bars

iPhone Duo's wider aspect ratio allows controls that normally appear in top and bottom bars to move to a side bar, preserving vertical content space while keeping controls reachable. The same bar components adapt between vertical and horizontal layouts depending on display posture and orientation.

To get the new behavior, rebuild against the latest SDKs and rely on bars managed by system navigation containers. Custom standalone UIKit bars such as manually created UIToolbar, UINavigationBar, or UITabBar instances are not considered for the vertical bar behavior.

  • SwiftUI: pair toolbar content with NavigationStack, NavigationSplitView, or tab containers.
  • UIKit: prefer UINavigationController and UITabBarController, and configure items through the owning view controller or navigation item.
  • Do not expect content from custom-built bar subcomponents to be automatically unified into the iPhone Duo side bar.

SwiftUI toolbar content inside a navigation container

Toolbar items participate in the adaptive bar behavior when attached to system navigation containers.

var body: some View {
    NavigationStack {
        ContentView()
            .toolbar {
                ToolbarItem(placement: .bottomBar) {
                    // action
                }
            }
    }
}

Prefer controller-managed UIKit bars

Use navigation and tab bar controllers instead of custom bar instances when preparing for iPhone Duo.

// Content from a custom UIToolbar won't be considered.
// Prefer UINavigationController and UITabBarController.
let toolbar = UIToolbar()
toolbar.items = [...]

Understand the shared vertical bar region

Navigation, toolbar, and tab bar controls can coexist in a shared side region. A useful mental model is a 90-degree rotation into a vertical stack, while preserving item grouping and container ownership.

More complex layouts have constraints. In split views, only the detail column participates; items in other columns remain horizontal. Inspectors do not receive a separate vertical bar when expanded, avoiding competing side bars. Sheet behavior depends on display and placement: on the outer display an existing sheet toolbar can display vertically, while centered sheets on the inner display keep items horizontal unless placement changes put them on the appropriate side.

  • The side bar stays aligned to the hardware edge, including in right-to-left languages.
  • Keep controls associated with their container rather than forcing every control onto the vertical axis.
  • Maintain consistent placement so actions do not move unpredictably as device posture changes.

Order navigation and prominent actions deliberately

Vertical bars preserve a top-to-bottom hierarchy. Audit existing toolbar configuration before adopting the layout, especially for custom back, close, done, and other prominent actions.

Primary navigation belongs at the top. Prominent completion or high-emphasis actions should follow using the new pinned trailing placements. Remaining items keep their original grouping, with visual separation between top and bottom placements even when they are unified into one vertical region.

  • Back buttons provided by UINavigationController are added automatically.
  • For custom back or close buttons, use SwiftUI's cancellationAction placement.
  • For UIKit custom leading items, keep leftItemsSupplementBackButton false when replacing rather than supplementing the back button.
  • Use topBarPinnedTrailing in SwiftUI or pinnedTrailingGroup in UIKit for prominent trailing actions.

Place custom back or close controls

Use the recommended navigation placement so primary navigation appears at the top of the vertical hierarchy.

// SwiftUI
.toolbar {
    ToolbarItem(placement: .cancellationAction) {
        // close or cancel
    }
}

// UIKit
navigationItem.leftItemsSupplementBackButton = false
navigationItem.leadingItemGroups = [UIBarButtonItemGroup(...)]

Pin prominent actions

Pinned trailing placement expresses that an action should remain prominent in the vertical bar.

// SwiftUI
.toolbar {
    ToolbarItem(placement: .topBarPinnedTrailing) {
        // done or prominent action
    }
}

// UIKit
navigationItem.pinnedTrailingGroup = UIBarButtonItemGroup(...)

Prepare toolbar content and custom views for a vertical axis

Vertical bars have fixed width and flexible item height, so symbol-only items work best. Continue providing both an icon and title where possible: the system uses the icon for compact visible representations and the title in overflow menus or expanded forms.

The system now also considers whether an item is suitable for a vertical or horizontal axis. Icon-backed items can move vertically, text-only items stay horizontal, and complex custom views stay horizontal by default unless you opt them into a vertical representation.

  • Always provide a title, even for image-only items, so overflow and expanded presentations remain accessible and clear.
  • Use AxisBehavior when defaults are not appropriate.
  • Keep transitional controls, such as an item that changes between a symbol and text, on the horizontal axis.
  • Prefer badges over inline count text when the text only supplements a symbol.
  • Keep meaningful standalone text, such as a cart total, in a horizontal representation.

Control the axis of toolbar items

Use horizontalOnly for controls that should not rotate into the side bar, and verticalPreferred for custom views that support a vertical layout.

// SwiftUI
ToolbarItem {
    SelectOrDoneButton()
}
.axisBehavior(.horizontalOnly)

ToolbarItem {
    CompassView()
}
.axisBehavior(.verticalPreferred)

// UIKit
item.axisBehavior = .horizontalOnly
item.axisBehavior = .verticalPreferred

Use a badge instead of inline text

Badges, added in iOS 26, can convert a text-and-symbol control into a more vertical-friendly symbol-only item with standard badge styling.

// SwiftUI
InboxButton()
    .badge(7)

// UIKit
let item = UIBarButtonItem(...)
item.badge = .count(7)

Adapt custom views and spacing

Custom views need to either fit the fixed width of the vertical bar or provide an adapted vertical layout. The session's example hides titles and reduces height for a custom action panel when vertical, leaving more space for other items.

Use the vertical bar environment value or trait to detect whether vertical placement is available, and avoid adding extra spacing that fights the system layout. Vertical bars do not have a scroll edge effect by default, but they do show a background when Reduce Transparency is enabled, so custom content must remain legible in both cases.

  • SwiftUI custom views can read toolbarVerticalEdge from the environment.
  • UIKit code can inspect traitCollection.verticalBarEdge.
  • Flexible spacers have zero size on the vertical axis; fixed spacers continue to respect their minimum size.
  • Keyboard accessory bars should remain attached to the keyboard rather than moving to the vertical axis.

Read the vertical bar edge

Use the vertical edge signal in content or custom item views to adapt layouts when a vertical bar can be present.

// SwiftUI
struct ContentView: View {
    @Environment(\.toolbarVerticalEdge) var edge

    var body: some View {
        switch edge {
        // adapt layout
        }
    }
}

// UIKit
switch traitCollection.verticalBarEdge {
// adapt layout
}

Manage overflow and visibility priority

Items overflow more often on the outer display in landscape and when competing UI, such as the keyboard or picture-in-picture, reduces available space. Decide whether toolbar items or the tab bar should stay visible longer for each view.

Navigation-focused experiences usually keep the tab bar visible and let the toolbar compress first, which is the default. Task-oriented experiences may prefer preserving frequent actions by compressing the tab bar first. If the app has a custom overflow menu, consolidate those actions into the system-managed overflow and reserve the ellipsis symbol for overflow.

  • Use toolbarVerticalCompressionBehavior in SwiftUI or verticalBarCompressionBehavior in UIKit to express compression preference.
  • Use ToolbarOverflowMenu in SwiftUI or additionalOverflowItems in UIKit to add persistent overflow actions.
  • Toolbar items overflow from bottom to top by default.
  • Assign high, low, or custom visibility priorities, first by groups and then by items when needed.
  • Keep frequently used actions and status-bearing items, such as badges, visible longer.

Configure compression behavior

Set which bar content should be preserved longer when vertical space becomes constrained.

// SwiftUI
ContentView()
    .toolbarVerticalCompressionBehavior(.prefersToolbarItems)

// UIKit
navigationItem.verticalBarCompressionBehavior = .prefersBarItems

Add overflow actions and prioritize items

Use the system overflow menu and visibility priorities to control which actions remain visible longest.

// SwiftUI
.toolbar {
    ToolbarOverflowMenu {
        Button("Scan") { }
        Button("Connect") { }
    }

    ToolbarItem {
        Button("Compose") { }
    }
    .visibilityPriority(.high)
}

// UIKit
navigationItem.additionalOverflowItems = UIDeferredMenuElement { provider in
    provider(self.persistentOverflowItems())
}

let item = UIBarButtonItem(...)
item.visibilityPriority = .high

Know when to disable vertical bars

Most apps are good candidates for vertical bars, but not every screen benefits. A single-page, bottom-heavy layout such as Calculator may make better use of a horizontal bar so the content can expand. A sheet with only one control, such as a close button, may not justify losing side space to a vertical bar.

  • Evaluate whether the vertical bar improves usable content space and action reachability for each screen.
  • Disable the behavior selectively rather than avoiding system bars broadly.

Disable the vertical bar

Use the vertical bar behavior APIs for screens where the horizontal layout is a better fit.

// SwiftUI
NavigationStack {
    ContentView()
        .toolbarVerticalBehavior(.disabled)
}

// UIKit
class MyViewController: UIViewController {
    override var preferredVerticalBarBehavior: UIVerticalBarBehavior {
        .disabled
    }
}
Unofficial, not associated with Apple. Made with ❤️ by

On this page

Ask AI