On the folded iPhone Duo display, a navigation bar item briefly moves into the vertical bar during dismissal of a full-screen modal, then jumps back to the top. Both navigation controllers consistently return .disabled from preferredVerticalBarBehavior, with childForPreferredVerticalBarBehavior returning nil.
Environment Xcode 27.1 RC (27A9275), iOS 27.1 SDK, iPhone Duo simulator, folded outer display in portrait.
Steps to reproduce
- Run the single-file UIKit example below in a scene-based app with a generated launch screen and no main storyboard.
- Tap the gear button on the Home screen to present Settings using .fullScreen.
- Tap the close button on Settings. Watch the Home screen's gear button during the dismissal animation.
Expected The gear button stays in the top horizontal navigation bar throughout the transition, because the vertical bar preference is .disabled on both navigation controllers.
Actual For part of the dismissal, the gear button appears below the status icons in the right-hand vertical bar region. It then jumps back to the top horizontal bar. The underlying tab bar also temporarily moves from the bottom to the right before returning.
This reproduces with an otherwise empty view, standard UIKit containers, and the default bar appearance. No app code changes safeAreaInsets, bar frames, or animation timing. During the presentation/dismissal cycle, the Home view's logged right safe-area inset changes from 0 to 88 points and back to 0. In our app, content constrained to the safe area visibly narrows during the same transient change.
In separate comparisons, returning .disabled from the root tab controller and content controllers, or requesting setNeedsUpdateOfVerticalBarConfiguration in appearance callbacks, did not eliminate the issue. Changing the modal to .overFullScreen avoided it in the tested simulator, but changes the underlying view controller lifecycle. The example below intentionally keeps .fullScreen to reproduce the problem.
Is this expected behavior, or a known UIKit issue? Is there a supported way to keep .disabled effective throughout a fullScreen presentation/dismissal? Has this been addressed in a newer SDK/runtime? I have not tested iOS 27.2 or a physical Duo device.
Minimal reproduction (one Swift file):
import UIKit
@main
final class DemoAppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, configurationForConnecting session: UISceneSession,
options: UIScene.ConnectionOptions) -> UISceneConfiguration {
let configuration = UISceneConfiguration(name: "Demo", sessionRole: session.role)
configuration.delegateClass = DemoSceneDelegate.self
return configuration
}
}
final class DemoSceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession,
options: UIScene.ConnectionOptions) {
guard let scene = scene as? UIWindowScene else { return }
let home = DemoController()
home.title = "首页"
let navigation = DemoNavigationController(rootViewController: home)
navigation.tabBarItem = UITabBarItem(title: "首页", image: UIImage(systemName: "house"), selectedImage: nil)
let tabs = UITabBarController()
if #available(iOS 18.0, *) { tabs.mode = .tabBar }
tabs.viewControllers = [navigation]
let window = UIWindow(windowScene: scene)
window.rootViewController = tabs
self.window = window
window.makeKeyAndVisible()
}
}
final class DemoNavigationController: UINavigationController {
// 与音剪一致:导航容器固定横向栏,不转发给子页面。
@available(iOS 27.1, *)
override var preferredVerticalBarBehavior: UIVerticalBarBehavior { .disabled }
@available(iOS 27.1, *)
override var childForPreferredVerticalBarBehavior: UIViewController? { nil }
}
final class DemoController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
if title == "设置" {
navigationItem.leftBarButtonItem = UIBarButtonItem(systemItem: .close, primaryAction: UIAction { [weak self] _ in
self?.dismiss(animated: true)
})
} else {
navigationItem.rightBarButtonItem = UIBarButtonItem(
image: UIImage(systemName: "gearshape"), primaryAction: UIAction { [weak self] _ in
let settings = DemoController()
settings.title = "设置"
let navigation = DemoNavigationController(rootViewController: settings)
navigation.modalPresentationStyle = .fullScreen
self?.present(navigation, animated: true)
})
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
print("[DuoDemo] \(title ?? "") safe=\(view.safeAreaInsets) nav=\(navigationController?.navigationBar.frame ?? .zero) tab=\(tabBarController?.tabBar.frame ?? .zero)")
}
}