iOS UIKit中能否在present完成前调用dismiss?异步场景问询
dismiss() Immediately After present() on UIAlertController: Errors & Fixes Will this cause runtime errors?
Short answer: No, it won’t trigger a crash, but you’ll get wonky behavior that’s bad for users. For example:
- The alert might never appear on screen (it gets dismissed before the present animation finishes)
- The present/dismiss animations might glitch or cut off abruptly
- You might see warning logs from the system about incomplete view controller transitions
It’s not a crash, but it’s definitely not a good user experience.
How to delay dismiss() until present() completes (without using the present completion callback)
Since you mentioned the dismiss() comes from an independent async task’s completion handler, we can’t rely on the present(_:animated:completion:) callback directly. Here are a few reliable approaches:
1. Use the Transition Coordinator (Cleanest & Most System-Friendly)
Every view controller transition has a transitionCoordinator that lets you hook into the end of the animation. This method automatically handles whether the present transition is still in progress or already done:
let alert = UIAlertController(title: "title", message: "message", preferredStyle: .alert) self.present(alert, animated: true, completion: nil) // Capture the transition coordinator for later use let transitionCoordinator = alert.transitionCoordinator // Your async task's completion handler yourAsyncTask { [weak alert, weak transitionCoordinator] in guard let alert = alert else { return } DispatchQueue.main.async { if let coordinator = transitionCoordinator { // If the present animation is still running, wait for it to finish before dismissing coordinator.animate(alongsideTransition: nil) { _ in alert.dismiss(animated: true) } } else { // Present animation is already done — dismiss immediately alert.dismiss(animated: true) } } }
2. KVO to Watch for the Alert’s Window Attachment
You can observe when the alert’s view is added to a window (which happens once the present transition completes):
let alert = UIAlertController(title: "title", message: "message", preferredStyle: .alert) self.present(alert, animated: true, completion: nil) // Your async task's completion handler yourAsyncTask { [weak alert] in guard let alert = alert else { return } DispatchQueue.main.async { if alert.view.window != nil { // Alert is already presented — dismiss right away alert.dismiss(animated: true) } else { // Wait for the alert to be attached to a window, then dismiss var observation: NSKeyValueObservation? observation = alert.observe(\.view.window, options: .new) { [weak alert] _, change in guard let alert = alert, change.newValue != nil else { observation?.invalidate() return } observation?.invalidate() alert.dismiss(animated: true) } } } }
3. Track Presentation State with a Flag (Last Resort)
If you prefer a simpler (though slightly less robust) approach, you can use a boolean flag to mark when the present transition finishes:
let alert = UIAlertController(title: "title", message: "message", preferredStyle: .alert) var isAlertFullyPresented = false self.present(alert, animated: true) { isAlertFullyPresented = true } // Your async task's completion handler yourAsyncTask { [weak alert] in guard let alert = alert else { return } DispatchQueue.main.async { if isAlertFullyPresented { alert.dismiss(animated: true) } else { // Wait a tiny bit for the present animation to finish (not ideal, but works in most cases) DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { alert.dismiss(animated: true) } } } }
⚠️ Note: The asyncAfter approach is fragile — hardcoding delays can break if animation durations change across iOS versions or devices.
Final Recommendation
Stick with the Transition Coordinator method — it’s the most reliable and uses Apple’s built-in APIs to handle transition timing correctly, no hacks required.
内容的提问来源于stack exchange,提问作者ehmjaysee

