Keywords: iOS Development | View Controller | Window Hierarchy | Modal Presentation | Lifecycle Management
Abstract: This article provides a comprehensive examination of the common 'view not in window hierarchy' warning in iOS development, analyzing the critical relationship between view controller lifecycle and presentation timing. Through comparative analysis of viewDidLoad and viewDidAppear methods with detailed code examples, it explains proper modal view controller presentation logic. The article also discusses solutions for repeated presentation issues and state management strategies, offering practical technical guidance for iOS developers.
Problem Phenomenon and Background Analysis
During iOS application development, developers frequently encounter warning messages like: <code>Warning: Attempt to present < finishViewController: 0x1e56e0a0 > on < ViewController: 0x1ec3e000> whose view is not in the window hierarchy!</code>. This warning indicates an attempt to present a view controller when the presenting view controller's view has not yet been added to the window hierarchy.
View Controller Lifecycle and Presentation Timing
Understanding view controller lifecycle management is crucial to addressing this issue. When a view controller finishes loading, the system calls the <code>viewDidLoad</code> method, at which point the view controller's view is created but not yet added to the window's view hierarchy. Only when the view actually appears on screen, specifically when the <code>viewDidAppear:</code> method is called, does the view officially join the window hierarchy.
The following code example demonstrates incorrect presentation timing:
override func viewDidLoad() {
super.viewDidLoad()
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let finished = storyboard.instantiateViewController(withIdentifier: "finishViewController")
self.present(finished, animated: false, completion: nil)
}
The correct approach is to move the presentation logic to the <code>viewDidAppear:</code> method:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let finished = storyboard.instantiateViewController(withIdentifier: "finishViewController")
self.present(finished, animated: false, completion: nil)
}
Repeated Presentation Issues and State Management
While moving presentation logic to <code>viewDidAppear:</code> resolves the window hierarchy issue, it may introduce a new problem: the modal view controller will be presented every time the view appears, causing repeated presentations.
The solution involves implementing state management mechanisms:
class ViewController: UIViewController {
private var shouldPresentModal = true
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if shouldPresentModal {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let finished = storyboard.instantiateViewController(withIdentifier: "finishViewController")
self.present(finished, animated: false, completion: nil)
shouldPresentModal = false
}
}
}
Hierarchy Issues in Delegate Patterns
In practical development, communication between different view controllers through delegate patterns is common. The referenced article demonstrates hierarchy issues encountered when trying to display alert controllers on the original view controller through delegate callbacks after QR code scanning completion.
The core issue is that when a modal view controller is dismissed, the original view controller may not have fully restored its position in the window hierarchy. Attempting to present a new view controller immediately at this point will trigger warnings.
The solution is to ensure presentation operations occur only after the view controller has completely returned to the foreground:
protocol ImportCodeDelegate: AnyObject {
func displayAlert(text: String)
}
class MainViewController: UIViewController, ImportCodeDelegate {
func displayAlert(text: String) {
// Use DispatchQueue to ensure execution on main thread
DispatchQueue.main.async {
let alert = UIAlertController(title: "Alert", message: text, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alert, animated: true)
}
}
}
Best Practices and Conclusion
Properly handling view controller presentation timing requires consideration of multiple factors: view controller lifecycle stages, window hierarchy status, user interaction flows, etc. Developers should:
- Avoid presenting modal view controllers in <code>viewDidLoad</code>
- Prevent repeated presentations when presenting in <code>viewDidAppear:</code>
- Ensure view controllers have fully recovered when presenting through delegate callbacks
- Use state variables to control execution conditions of presentation logic
- Execute all UI-related operations on the main thread
By following these best practices, developers can effectively avoid 'view not in window hierarchy' related warnings and build more stable and reliable iOS applications.