Query
Find out how to keep away from an surprising structure animation throughout push transition on iOS 26? Is there any attainable workaround?
Particulars
Having a master-detail view with a navigation stack was once applied by having a UISplitViewController with 2 navigation controllers.
In compact structure these 2 controllers magically "collapse" into 1 by the UISplitViewController, and we are able to kind a primary navigation stack: grasp -> element -> sub-detail -> sub-sub-detail.
This setup used to work effectively earlier than iOS 26.
On iOS 26 the navigation push transition is buggy on sub-levels resulting from 2 separate structure points:
- When the transition begins, the pushed view controller structure isn’t up to date. The preliminary dimension (earlier than an replace) is .zero, and consequently we see the way it animates to the ultimate dimension and place. Watch how on the display recording the blue view expands from .zero.
- When the transition begins, the protected space structure information isn’t up to date. The protected space preliminary dimension is .zero. If a pushed view controller structure depends on it, we see the way it animates to accommodate the protected space dimension and place. Watch how on the display recording the pink view shrinks from full display dimension.
These animations are usually not anticipated! The pushed view controller structure must be fastened by the constraints throughout the push transition (because it was once earlier than iOS 26).
Bug (1) might be workarounded by including a layoutIfNeeded() name inside viewDidLoad() (see SubSubDetailViewController under).
However (2) is hard to workaround.
What have been tried:
-
Change viewControllers setup with utilizing a more moderen iOS 14 API, this had no impact – the identical bugs:
tremendous.init(model: .doubleColumn) self.setViewController(MasterViewController(), for: .major) self.setViewController(DetailViewController(), for: .secondary) -
Use a single UINavigationController unwrapping both MasterViewController or DetailViewController from having a UINavigationController round. This has undesired results in non-compact master-detail mode (seen on iPad and enormous iPhone’s).
-
Utilizing a prime constraint associated to the navigation bar backside anchor as a substitute of safeAreaLayoutGuide isn’t accounting for the left and proper edges notch areas (on panorama telephones).
-
Having one particular code model on small iPhones and one other code on different gadgets based mostly on the system mannequin is undesirable, as a result of iPad multitasking can be utilized to modify to compact structure mode dynamically.
To breed create an iOS mission in Xcode 26, paste this code, and run on an iOS 26.0 small iPhone simulator:
import UIKit
class ViewController: UISplitViewController {
required init?(coder: NSCoder) {
tremendous.init(coder: coder)
self.viewControllers = [
UINavigationController(rootViewController: MasterViewController()),
UINavigationController(rootViewController: DetailViewController()),
]
}
class MasterViewController: UIViewController {
override func viewDidLoad() {
tremendous.viewDidLoad()
self.title = "grasp"
self.view.backgroundColor = .pink
}
}
class DetailViewController: UIViewController {
override func viewDidLoad() {
tremendous.viewDidLoad()
self.title = "element"
self.view.backgroundColor = .orange
addBox(to: self.view).backgroundColor = .yellow
}
override func viewDidAppear(_ animated: Bool) {
tremendous.viewDidAppear(animated)
Timer.scheduledTimer(withTimeInterval: 3, repeats: false) { _ in
assert(self.navigationController != nil)
self.navigationController?.pushViewController(SubDetailViewController(), animated: true)
}
}
}
class SubDetailViewController: UIViewController {
override func viewDidLoad() {
tremendous.viewDidLoad()
self.title = "sub element"
self.view.backgroundColor = .inexperienced
addBox(to: self.view).backgroundColor = .cyan
}
override func viewDidAppear(_ animated: Bool) {
tremendous.viewDidAppear(animated)
Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in
assert(self.navigationController != nil)
self.navigationController?.pushViewController(SubSubDetailViewController(), animated: true)
}
}
}
class SubSubDetailViewController: UIViewController {
override func viewDidLoad() {
tremendous.viewDidLoad()
self.title = "sub sub element"
self.view.backgroundColor = .blue
addBox(to: self.view).backgroundColor = .magenta
}
override func viewWillAppear(_ animated: Bool) {
tremendous.viewWillAppear(animated)
self.view.layoutIfNeeded()
}
}
func addBox(to mother or father: UIView) -> UIView {
let field = UIView()
field.backgroundColor = .yellow
field.translatesAutoresizingMaskIntoConstraints = false
mother or father.addSubview(field)
let container = mother or father.safeAreaLayoutGuide
NSLayoutConstraint.activate([
NSLayoutConstraint(item: box, attribute: .top, relatedBy: .equal, toItem: container, attribute: .top, multiplier: 1, constant: 0),
NSLayoutConstraint(item: box, attribute: .bottom, relatedBy: .equal, toItem: container, attribute: .bottom, multiplier: 1, constant: 0),
NSLayoutConstraint(item: box, attribute: .leading, relatedBy: .equal, toItem: container, attribute: .leading, multiplier: 1, constant: 0),
NSLayoutConstraint(item: box, attribute: .trailing, relatedBy: .equal, toItem: container, attribute: .trailing, multiplier: 1, constant: 0),
])
return field
}
![Find out how to keep away from UINavigationController push transition protected space animation in compact UISplitViewController on iOS 26? [closed] Find out how to keep away from UINavigationController push transition protected space animation in compact UISplitViewController on iOS 26? [closed]](https://i.sstatic.net/1X4GUf3L.gif)

