首页 > 解决方案 > 为什么在 UINavigationController 上关闭 UIAlertController 调用关闭?

问题描述

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

原因是我从 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:更新代码以更好地表示场景。

标签: iosswiftuikit

解决方案


原因是导航控制器是显示警报控制器的控制器,即使您调用present视图控制器也是如此。通话也是如此dismiss

如果您希望视图控制器显示警报,请将其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,将默认值更改为 true。

更新:

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

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

推荐阅读