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

Posted

技术标签:

【中文标题】如何正确关闭作为模式呈现的 UINavigationController?【英文标题】:How to correctly dismiss a UINavigationController that's presented as a modal? 【发布时间】:2016-02-15 18:28:24 【问题描述】:

在我的TabBarViewController 中,我创建了一个 UINavigationController 并将其呈现为模式。

var navController =  UINavigationController()
let messageVC = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
self.presentViewController(self.navController, animated: false, completion: nil)
self.navController.pushViewController(messageVC, animated: false)

在我的MessageViewController 中,我想这样关闭它:

func swipedRightAndUserWantsToDismiss()
    if self == self.navigationController?.viewControllers[0] 
        self.dismissViewControllerAnimated(true, completion: nil) //doesn't deinit
    else
        self.navigationController?.popViewControllerAnimated(true) //deinits correctly
    


deinit
    print("Deinit MessagesViewController")

问题是,当我到达根视图控制器并尝试关闭子视图控制器和 UINavigationController 时,我的 MessagesViewController deinit 不会被调用。有什么东西在控制它——很可能是 UINavigationController

【问题讨论】:

【参考方案1】:

您的控制器层次结构如下所示:

UITabViewController
    |
    | presents
    |
UINavigationController
    |
    | contains view controllers
    |
[root, MessagesViewController]

现在,如果你在 MessagesViewController 内部,那么它的 navigationController 就是正在呈现的那个,这就是你应该解雇的那个,但是在 MessagesViewController 上调用 dismiss 也应该可以工作。

但是,问题在于解除导航控制器不会删除其视图控制器。看来您正握着导航控制器(因为您使用 self.navController 呈现它)所以状态将变为

UITabViewController
    |
    | self.navController holds a reference to
    |
UINavigationController
    |
    | contains view controllers
    |
[root, MessagesViewController]

要正确销毁MessagesViewController,您必须要么放开navController,要么必须弹出到根目录(从而从视图层次结构中删除MessagesViewController)。

典型的解决方案是根本不保存对navController 的引用。您可以在演示时创建一个新的UINavigationController。 另一种解决方案是使用委托 - 而不是从 MessagesViewController 内部解散,而是让它回调到演示者,后者将调用

self.navController.dismiss(animated: true) 
     self.navController = nil

【讨论】:

谢谢。如果我在堆栈中有许多视图控制器,但我的委托调用 dismissViewControllerAnimated 然后将其设置为 nil ,它会取消 所有 堆栈中的视图控制器吗? 当你放弃对navController的所有引用时,如果你不在其他地方持有它们,它的所有视图控制器都将被销毁。 您也无法关闭已设置为 rootviewcontroller 的视图控制器。所以,如果:【参考方案2】:

试试这个

func swipedRightAndUserWantsToDismiss()
    self.navigationController.dismissViewControllerAnimated(false, completion:nil);

【讨论】:

【参考方案3】:

您可以使用以下方法正确关闭在 Swift 4 中显示为模式的 UINavigationController

self.navigationController?.popViewController(animated: true)

【讨论】:

【参考方案4】:

如果您只想呈现一个视图控制器,那么您可以直接呈现该视图控制器,而无需为该特定视图控制器获取导航控制器。

但是当我们需要从显示的视图控制器导航时,我们需要将视图控制器作为导航控制器的根视图。这样我们就可以从显示的视图控制器中导航。

let messageVC = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
let MynavController = UINavigationController(rootViewController: messageVC)
self.presentViewController(MynavController, animated: true, completion: nil)

从那个呈现的视图控制器,你可以推送到另一个视图控制器,也可以从另一个视图控制器弹出。

从呈现的视图控制器,这里是messageVC,我们必须将其视为

func swipedRightAndUserWantsToDismiss() 
  self.dismiss(animated: true, completion: nil)

这将成功解除messageVC 并返回到我们提供messageVC 的原始视图控制器。

这是使用导航控制器执行presentViewController 以继续在视图控制器之间导航的正确流程。

如果您不确定是否显示或推送了 messageVC,请查看by this answer。

要检查的快速版本是

func isModal() -> Bool 
    if((self.presentingViewController) != nil) 
        return true
    

    if(self.presentingViewController?.presentedViewController == self) 
        return true
    

    if(self.navigationController?.presentingViewController?.presentedViewController == self.navigationController) 
        return true
    

    if((self.tabBarController?.presentingViewController?.isKindOfClass(UITabBarController)) != nil) 
        return true
    

    return false

所以我们最后的解雇动作是这样的

func swipedRightAndUserWantsToDismiss() 

            if self.isModal() == true 
                self.dismiss(animated: true, completion: nil)
            
            else 
                self.navigationController?.popViewControllerAnimated(true)
            

        

【讨论】:

【参考方案5】:

不需要拥有 navController 的成员。使用以下代码展示您的 MessagesViewController。

let messageVC = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
let pesentingNavigationController = UINavigationController(rootViewController: messageVC)
self.presentViewController(pesentingNavigationController, animated: true, completion: nil)

您的关闭视图控制器代码将是

func swipedRightAndUserWantsToDismiss() 
  self.navigationController.dismiss(animated: true, completion: nil)

【讨论】:

【参考方案6】:

我建议你为你的UINavigationController 使用另一个初始化器:

let messageVC = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
let navController = UINavigationController(rootViewController: messageVC)
self.presentViewController(self.navController, animated: true, completion: nil)

要辞退,只需做

func swipedRightAndUserWantsToDismiss() 
  self.navigationController.dismissViewControllerAnimated(true, completion: nil)

【讨论】:

【参考方案7】:

这就是我在 Objective C 中解决问题的方法。

您可以在 self.navigationController 本身上调用 dismissViewControllerAnimated:NO

目标 C

[self.navigationController dismissViewControllerAnimated:NO completion:nil];

斯威夫特

self.navigationController.dismissViewControllerAnimated(false, completion: nil)

【讨论】:

【参考方案8】:

在 Swift 3 中,这是通过以下方式实现的:

self.navigationController?.dismiss(animated: true, completion: nil)

【讨论】:

以上是关于如何正确关闭作为模式呈现的 UINavigationController?的主要内容,如果未能解决你的问题,请参考以下文章

模态视图的奇怪位置

呈现和关闭模式视图控制器时如何保持选择集合视图单元格?

如何正确关闭模态呈现的控制器

如何在 swift 中呈现自定义模式 [关闭]

Swift 如何在关闭模式后在根导航中呈现视图

下载为 html 时的 Jupyter 笔记本暗模式无法正确呈现