# UIKit on iPhone Duo: vertical bars and arrangements > What changes in a UIKit app on iPhone Duo: the vertical bar region, UIArrangementViewController, reserved regions, and the UIScreen.main deprecation. Source: https://iphoneduosupport.com/blog/uikit-iphone-duo-vertical-bars/ Published: 2026-09-09 Framework: uikit Status: Apple-documented — every claim traces to an Apple source. Sources: https://developer.apple.com/videos/play/tech-talks/111461/ https://developer.apple.com/videos/play/tech-talks/111462/ https://developer.apple.com/videos/play/tech-talks/111463/ Last verified: 2026-09-09 --- UIKit apps have an advantage on iPhone Duo that is easy to miss: if your app already supports iPad, most of the work is done. The inner display is regular-width in both dimensions, which is the same environment your iPad layout already targets. The apps with real work ahead are iPhone-only ones, and apps that replaced the system bars with their own. ## UIScreen.main stops making sense Start here, because it is the change with the widest blast radius. Apple stated in *Prepare your app for iPhone Duo* that main screen references will be deprecated and should be avoided on two-display devices. This is not a style preference. iPhone Duo has two displays with **different pixel densities** — 430 ppi inner, 460 ppi outer — and your app can move between them while running. A global "main screen" cannot answer which display a given view is actually on. ```swift // Before let scale = UIScreen.main.scale let bounds = UIScreen.main.bounds // After let scale = traitCollection.displayScale let screen = view.window?.windowScene?.screen ``` Scale is a trait, so it updates automatically as the view moves between displays. That is the whole point of reading it from the trait collection rather than a global. For layout, do not reach for the screen at all. `view.bounds` and the safe area describe the space you were given — which in Split View is smaller than the display regardless of which one you are on. ```bash grep -rn "UIScreen.main" --include="*.swift" . ``` Run that first. It is usually a bigger list than people expect, and much of it is in code nobody has opened in years. ## Safe areas are asymmetric On every previous iPhone, left and right insets were mirror images. That made a shortcut look safe: ```swift // Wrong on iPhone Duo let width = view.bounds.width - view.safeAreaInsets.left * 2 ``` The status bar and camera sit asymmetrically on the inner display, and the hinge introduces geometry with no mirror. Inset the rectangle instead of doing arithmetic on one side: ```swift let width = view.bounds.inset(by: view.safeAreaInsets).width ``` Apple's split is worth stating plainly, because it is what makes an app look finished: ```swift // Interactive content — inside the safe area foreground.frame = view.bounds.inset(by: view.safeAreaInsets) // Background artwork — full bounds backgroundView.frame = view.bounds ``` Applying one policy to a whole screen is the mistake. A card with a background image wants the image to bleed and the text to stay inset. ## The vertical bar region This is the most visible change under the iOS 27.1 SDK. On the inner display in landscape, navigation, toolbar, and tab bar controls move into a **single shared vertical region** at the side. Picture your bars rotated ninety degrees into one column. You get this automatically with `UINavigationController` and `UITabBarController`. **Custom `UIToolbar` content is not considered.** If you replaced the system bars with your own — a common decision in apps with strong visual identity — you are outside the adaptive path and will need to rebuild on the system containers to get back into it. Two details worth knowing: the bar stays on the same side in right-to-left languages, and in a split view only the detail column participates. Inspectors do not get their own bar. ### Controlling it Placement, since order matters in a column: ```swift // Back / close goes to the top navigationItem.leftBarButtonItem = closeItem // .cancellationAction equivalent ``` Orientation of individual items: ```swift item.axisBehavior = .verticalPreferred item.axisBehavior = .horizontalOnly ``` Detecting the vertical case, so custom views can adapt: ```swift switch traitCollection.verticalBarEdge { … } ``` What compresses first when space runs out: ```swift navigationItem.verticalBarCompressionBehavior = .prefersBarItems ``` Which actions survive overflow: ```swift item.visibilityPriority = .high ``` And consolidating your own ellipsis menu into the system one, so users do not see two identical menus stacked: ```swift navigationItem.additionalOverflowItems = UIDeferredMenuElement({ provider in provider(self.persistentOverflowItems()) }) ``` Badges from iOS 26 carry over, and are useful for keeping a signal on an item that has lost its text label: ```swift item.badge = .count(7) ``` ### Opting out Some screens are worse for it. Apple names single-page layouts with heavy bottom content — a calculator — and sheets with only a close button: ```swift override var preferredVerticalBarBehavior: UIVerticalBarBehavior { .disabled } ``` Per screen, on purpose. Not globally. ## Arrangements `UIArrangementViewController` is the UIKit counterpart to SwiftUI's `ArrangementView`, and it handles the main-and-secondary case across every pose: ```swift let arrangementVC = UIArrangementViewController() let navController = UINavigationController(rootViewController: arrangementVC) arrangementVC.setViewController(PlayerViewController(), for: .primary) arrangementVC.setViewController(UpNextViewController(), for: .secondary) ``` Style is set the same way, and can be updated at runtime: ```swift arrangementVC.updateArrangement(.split.axes(.horizontal)) ``` For an overlay arrangement, read the z-index so the secondary view can collapse when stacked: ```swift let primaryState = arrangementVC.state(for: .primary) myModel.minimization = (primaryState?.zIndex ?? 0) > 0 ? .collapsed : .expanded ``` ## Reserved regions `UIViewReservedRegion` reports where the display is interrupted: ```swift let regions = view.reservedRegions(kind: .division) let frames = regions.map(\.frame) ``` Division regions divide — the hinge. Occlusion regions cover — the under-display camera. Division regions are **active only while folded and have zero width when flat**, so handle them appearing and disappearing, not just moving. Use arrangements first. Query regions directly when you need control an arrangement cannot give you. ## Multiple scenes iPhone Duo is the first iPhone supporting multiple app instances. Apps already supporting this on iPad get it here. ```xml UIApplicationSupportsMultipleScenes ``` Two constraints: new windows can only be created on the **inner display**, and Apple is explicit that you **must handle errors** when requesting a scene through `UIWindowSceneActivation`. The action hides itself when windows are unavailable, so a correctly built UI does not offer something that cannot work. If you enable it, audit anything assuming a single instance — singletons holding view state, global notification handling that presents UI, analytics assuming one session per launch. ## Split View is not optional All apps participate in side-by-side multitasking on iPhone Duo. An app that has never been resized can now be handed an arbitrary fraction of the display, at a moment the user chooses. ```swift override func viewWillTransition( to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator ) { super.viewWillTransition(to: size, with: coordinator) coordinator.animate { _ in self.relayout(for: size) } } ``` While you are there, check `UIRequiresFullScreen`. An app still carrying that key is fighting the platform. One failure mode that is easy to miss: in Split View your app can be **visible but not frontmost**. Timers, video, and animation stopped on `sceneWillResignActive` will look frozen to a user who can still see the window. ## Where to start 1. `grep -rn "UIScreen.main"` and fix every hit 2. `grep -rn "userInterfaceIdiom"` and check each one against size classes instead 3. Rebuild against the iOS 27.1 SDK 4. Run the App Resizability skill in Xcode 27.1 — it now covers this migration 5. Audit bar content for the vertical region 6. Adopt `UIArrangementViewController` where you have a primary/secondary screen Steps 1 and 2 are unglamorous and account for most of the difference between an app that behaves and one that does not. --- Published by iPhone Duo Support (https://iphoneduosupport.com). Not affiliated with Apple Inc.