I have a UITabBarController with multiple tabs. From the second tab's UINavigationController, I push a detail view controller (DetailVC).
Architecture and execution flow:
DetailVC is responsible for its own navigation lifecycle.
When a button is clicked in DetailVC, it calls navigationController?.popToRootViewController(animated: true) to pop back to the root view controller of the second tab (let's call it RootVC).
In RootVC's viewWillAppear, it checks the state and executes tabBarController.selectedIndex = 0 to switch to the first tab.
Here is a simplified simulation:
In DetailVC:
@IBAction func switchButtonClicked(_ sender: UIButton) {
// Step 1: Pop to root of second tab's navigation stack
navigationController?.popToRootViewController(animated: true)
// Step 2: The RootVC will handle this in viewWillAppear
DispatchQueue.main.async {
guard let windowScene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene,
let window = windowScene.windows.first(where: { $0.isKeyWindow }),
let tabBarController = window.rootViewController as? UITabBarController else {
return
}
// This simulates RootVC's viewWillAppear logic
// In production, RootVC would set this when it appears
tabBarController.selectedIndex = 0
}
}
Execution order:
DetailVC → popToRootViewController(animated: true)
→ Navigation stack pops to RootVC
→ RootVC.viewWillAppear is called
→ Inside viewWillAppear, tabBarController.selectedIndex = 0 is executed
→ Switch to first tab
The problem:
On iOS 18 below, this works perfectly — the transition to the first tab is seamless.
On iOS 18 and above, the selected index does switch to 0 correctly, but the tab bar rendering is noticeably delayed — it takes approximately one second to appear after the root view of the first tab has already loaded.
My questions:
Has Apple changed the timing of when viewWillAppear and selectedIndex changes are committed to the render pipeline in iOS 18? Specifically, does viewWillAppear now allow the view to lay out and render before the selectedIndex change takes effect?
Given this architectural pattern (popToRootViewController → RootVC.viewWillAppear → selectedIndex change), what is the recommended approach to ensure the tab switch happens before RootVC's view is rendered?
Given the complexity of our existing codebase and the number of features tied to this navigation flow, I'd strongly prefer to preserve this architectural pattern rather than refactoring the entire communication mechanism between DetailVC and RootVC. I'm looking for a robust, iOS 18-compatible solution that preserves the existing separation of concerns (DetailVC manages navigation, RootVC manages tab state via viewWillAppear) while eliminating the visible flash of the second tab's root view controller.
Thank you for your insights!