关闭ConfirmUser后尝试展示ChatController时出现弹窗冲突警告:Attempt to present ChatController on RoomViewController which is already presenting ConfirmUser
Hey there, let's break down why you're getting that warning: Attempt to present ChatController on RoomViewController (from RoomViewController) which is already presenting ConfirmUser, and how to fix it.
The Root Cause
This warning pops up because you're trying to present the ChatController before the ConfirmUser popup has fully finished dismissing and the system has updated the presenting state of your RoomViewController. Even though you're using the dismiss completion handler, sometimes the system's internal state takes a split second to catch up—so it still thinks your RoomViewController is actively presenting the old popup when you try to show the new controller.
Looking at Your Code
Here's your current implementation for reference:
confirmPopup.dismiss(animated: false, completion: { [self] in dialogIsShowing = false let story = UIStoryboard(name: "Chat", bundle: nil) let chat = story.instantiateViewController(withIdentifier: "chat_controller") as? ChatController chat?.roomCreator = byUser controller!.present(chat!, animated: true, completion: nil) })
A couple of red flags here:
- Using
controller!.presentrelies on an externalcontrollervariable—if that's yourRoomViewController, the system might not have updated itspresentedViewControllerstatus yet when the completion runs. - Force-unwrapping with
chat!is risky—if your Storyboard ID is wrong or the type conversion fails, this will crash your app.
The Fixes
Option 1: Check Presenting State & Use Safe Unwrapping
Let's adjust the code to use the current RoomViewController directly, add checks to make sure it's ready to present a new controller, and eliminate force-unwraps:
confirmPopup.dismiss(animated: false, completion: { [weak self] in guard let self = self else { return } self.dialogIsShowing = false let story = UIStoryboard(name: "Chat", bundle: nil) guard let chat = story.instantiateViewController(withIdentifier: "chat_controller") as? ChatController else { print("Oops—failed to load ChatController from Storyboard") return } chat.roomCreator = self.byUser // Make sure we're not still presenting anything before showing the new controller if self.presentedViewController == nil { self.present(chat, animated: true, completion: nil) } else { // Edge case: If there's still a presented controller, dismiss it first self.dismiss(animated: false) { self.present(chat, animated: true, completion: nil) } } })
- Using
[weak self]prevents memory leaks from the closure holding onto your controller. - The
presentedViewController == nilcheck ensures your controller is in a clean state to present the new one. guard letsafely loads theChatControllerwithout risking crashes from force-unwraps.
Option 2: Simplify by Dismissing from the Parent Controller
Since your confirmPopup is presented by RoomViewController, you can just call dismiss on the parent controller directly. This ensures the completion handler runs only after the popup is fully gone and the parent's state is updated:
self.dismiss(animated: false, completion: { [weak self] in guard let self = self else { return } self.dialogIsShowing = false let story = UIStoryboard(name: "Chat", bundle: nil) guard let chat = story.instantiateViewController(withIdentifier: "chat_controller") as? ChatController else { print("Oops—failed to load ChatController from Storyboard") return } chat.roomCreator = self.byUser self.present(chat, animated: true, completion: nil) })
This is a cleaner approach because calling self.dismiss() on the parent controller automatically handles dismissing any presented child controllers, and the completion runs once everything is cleared up.
Quick Extra Tips
- Ditch the force-unwraps (
!) wherever possible—they're a common source of crashes. Useguard letorif letinstead. - Always use
[weak self]in closures that reference your view controller to avoid memory leaks. - If you're working with multiple presented controllers, make sure to track their state to avoid weird presentation conflicts.
内容的提问来源于stack exchange,提问作者Besart

