My app has a split view with a root view and a detail view, both of which are inside their own navigation view controller. The navigation bar of the detail view can be hidden to allow the user to stay more focused on the content.
The problem is that when the navigation bar of the content view is hidden in portrait mode and the iPhone is rotated to landscape mode, the navigation bar of the content view becomes visible automatically and the sidebar appears on the left side, but the navigation bar of the sidebar is hidden.
Is it expected that the navigation bar becomes visible automatically when rotating the device? Why is the navigation bar of the sidebar hidden? Is this related to hiding the navigation bar of the content view and why does this happen? What am I supposed to do so that the navigation bar of the sidebar remains visible at all times?
For comparison the two cases:
- After rotation when navigation bar of content view was visible:
- After rotation when navigation bar of content view was hidden:
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
window = UIWindow(windowScene: scene as! UIWindowScene)
window!.rootViewController = SplitViewController()
window!.makeKeyAndVisible()
}
}
class SplitViewController: UISplitViewController, UISplitViewControllerDelegate {
var detailNavigationViewController: UINavigationController!
init() {
super.init(nibName: nil, bundle: nil)
detailNavigationViewController = UINavigationController(rootViewController: DetailViewController())
viewControllers = [UINavigationController(rootViewController: RootViewController())]
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func showDetail() {
showDetailViewController(detailNavigationViewController, sender: nil)
}
}
class RootViewController: UIViewController {
override func loadView() {
navigationItem.title = "Root"
navigationItem.rightBarButtonItem = UIBarButtonItem(systemItem: .add)
let button = UIButton(primaryAction: UIAction(handler: { [self] _ in
(splitViewController as! SplitViewController).showDetail()
}))
button.setTitle("Show detail", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
view = UIView()
view.layer.backgroundColor = UIColor(white: 0.9, alpha: 1).cgColor
view.addSubview(button)
NSLayoutConstraint.activate([NSLayoutConstraint(item: button, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1, constant: 0), NSLayoutConstraint(item: button, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1, constant: 0)])
}
}
class DetailViewController: UIViewController {
override func loadView() {
navigationItem.title = "Detail"
navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "sidebar.leading"), primaryAction: UIAction(handler: { _ in
UIView.animate(withDuration: 0.3) { [self] in
self.splitViewController!.preferredDisplayMode = .oneOverSecondary
}
}))
navigationItem.rightBarButtonItem = UIBarButtonItem(systemItem: .add)
let button = UIButton(primaryAction: UIAction(handler: { [self] _ in
navigationController!.setNavigationBarHidden(!navigationController!.isNavigationBarHidden, animated: true)
}))
button.setTitle("Toggle navigation bar", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
view = UIView()
view.addSubview(button)
NSLayoutConstraint.activate([NSLayoutConstraint(item: button, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1, constant: 0), NSLayoutConstraint(item: button, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1, constant: 0)])
}
}
I created FB24413608.
Hello @Nickkk,
Thank you for filing and providing your sample project. I shared your report with the relevant engineering team and they are aware of the issue.
Is it expected that the navigation bar becomes visible automatically when rotating the device?
No, it seems this behavior should only reproduce on larger devices where there is still a Split View Controller sidebar.
Updates will be provided to you through Feedback Assistant. Use this thread to communicate updates you find as you test new releases.
For more details on Feedback Status, please see “Understanding the Status of Your Feedback” linked here: https://developer.apple.com/bug-reporting/status
What am I supposed to do so that the navigation bar of the sidebar remains visible at all times?
They did not provide an explicit workaround with me directly, however you can try to explicitly re-assert the primary column's navigation bar visibility after each transition completes:
override func viewWillTransition(to size: CGSize, with coordinator: any UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: nil) { [weak self] _ in
guard let self, let primaryNav = self.viewControllers.first as? UINavigationController else { return }
primaryNav.setNavigationBarHidden(false, animated: false)
}
}
Place this override in your UISplitViewController subclass.
I hope this information is helpful.
Travis