为啥在 UINavigationController 上关闭 UIAlertController 调用关闭?

Posted

技术标签:

【中文标题】为啥在 UINavigationController 上关闭 UIAlertController 调用关闭?【英文标题】:Why dismissing a UIAlertController calls dismiss on UINavigationController?为什么在 UINavigationController 上关闭 UIAlertController 调用关闭? 【发布时间】:2018-12-21 13:13:20 【问题描述】:

我试图理解为什么当从导航中显示的视图控制器中关闭 UIAlertController 时,会调用 UINavigationController 上的 dimiss(animetaded:)

原因是我从 UINavigationController 继承了一些逻辑,以便在导航被解除时添加一些逻辑,但每次解除警报时都会无意中调用它。

据我了解,presentingViewController 负责关闭呈现的控制器,但这里似乎并非如此。

我错过了什么?

要重现,请运行下面的代码,它将记录消息“DISMISS ON NAVIGATION”。

    class RootViewController: UIViewController 
        override func viewDidAppear(_ animated: Bool) 
            super.viewDidAppear(animated)

            let alert = UIAlertController(title: "", message: "", preferredStyle: .actionSheet)
            alert.addAction(UIAlertAction(title: "cancel", style: .cancel, handler: nil))
            present(alert, animated: true)
        
    

    class Nav: UINavigationController 
        override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) 
            print("DISMISS ON NAVIGATION")
            super.dismiss(animated: flag, completion: nil)
        
    

现在从任何地方展示导航控制器。

            let contrl = RootViewController()
            contrl.view.backgroundColor = .red
            contrl.definesPresentationContext = true

            let nav = Nav(rootViewController: contrl)

            present(nav, animated: true, completion: nil)

编辑:更新代码以使 RootViewController 定义演示上下文。 Edit2:更新代码以更好地表示场景。

【问题讨论】:

【参考方案1】:

原因是导航控制器是显示警报控制器的控制器,即使您在视图控制器上调用presentdismiss 呼叫也是如此。

如果您希望视图控制器显示警报,请将其 definesPresentationContext 属性设置为 true

见https://developer.apple.com/documentation/uikit/uiviewcontroller/1621380-present...

您调用此方法的对象可能并不总是那个对象 处理演示文稿。每种演示风格都有不同的 支配其行为的规则。例如,全屏演示 必须由一个视图控制器来制作,该控制器本身覆盖整个 屏幕。如果当前视图控制器无法满足请求, 它将请求向上转发视图控制器层次结构到它的 最近的父级,然后可以处理或转发请求。

...和https://developer.apple.com/documentation/uikit/uiviewcontroller/1621456-definespresentationcontext:

当使用 UIModalPresentationStyle.currentContext 或 UIModalPresentationStyle.overCurrentContext 样式来呈现一个视图 控制器,此属性控制现有的视图控制器在 您的视图控制器层次结构实际上已被新内容覆盖。 当基于上下文的演示发生时,UIKit 从 呈现视图控制器并向上走视图控制器层次结构。 如果它找到了一个该属性的值为真的视图控制器, 它要求视图控制器呈现新的视图控制器。如果不 视图控制器定义表示上下文,UIKit 要求 窗口的根视图控制器来处理演示。 此属性的默认值为 false。一些系统提供的视图控制器,比如 UINavigationController,改变了默认值 值为真。

更新:

对于您的具体问题(如果我理解正确的话)也许是保持演示逻辑不变(导航控制器呈现)的最佳解决方案,而是在导航控制器的解除方法中添加一个检查:

override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) 
    if !(presentedViewController is UIAlertController) 
        // your additional logic
    
    super.dismiss(animated: flag, completion: completion)

【讨论】:

有道理,但即使在更新代码(见问题编辑)之后,行为也是一样的。在导航上调用关闭。 我对你的视图层次结构有点困惑......你能解释一下它是什么样的吗?有一个Nav 导航控制器包含RootViewController vc?那么ViewController 类呢?除此之外,您必须为 呈现 的视图控制器将 definesPresentationContext 设置为 true。不是呈现的 它是一个以 RootViewController 为根的 UINavigationControllerRootViewController 表示 UIAlertController。这里的 ViewController 可以是任何呈现导航的控制器。将更新解码代码以更好地表示它。 @MarcelodeAguiar 更新了我的答案。 尽管这解决了眼前的问题,但它并不能解释为什么会出现这种不稳定的行为,所以我还没有将其标记为答案。谢谢。

以上是关于为啥在 UINavigationController 上关闭 UIAlertController 调用关闭?的主要内容,如果未能解决你的问题,请参考以下文章

在 UINavigationController 中设置时图像不显示

将 managedObjectContext 发送到 viewController 崩溃

如何正确关闭作为模式呈现的 UINavigationController?

ID:[...] 的 NSManagedObject 已失效

使用 UINavigationController 从另一个控制器更新或重新加载 UIViewController?

UINavigationController - 何时释放推送的视图控制器等