Swift - 如何关闭所有视图控制器以返回到根视图

80

我希望我的应用程序可以在用户每次使用时都跳转到第一个视图控制器。

因此,我想创建一个函数以解除所有视图控制器的显示,无论它们是在导航控制器中推送、以模态方式呈现还是打开任何方法。

我尝试了各种方法,但我没有成功地确保解除所有视图控制器的显示。 有没有简单的方法?


2
你可以使用 NavigationController.popToRootController。 - Sagar Bhut
1
您可以更改根视图控制器。 - Amrit Tiwari
1
重置应用程序窗口的根视图控制器将解决问题。 - miOS
你正在使用segue吗?如果是的话,unwind segue是最简单的方法。 - Paulw11
谢谢大家。我整合了你们的意见并解决了它。 - Byoth
显示剩余2条评论
14个回答

98

试试这个:

self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)

它应该关闭所有在根视图控制器之上的视图控制器。

如果这样做不起作用,那么你可以像这样手动运行一个while循环来实现。

func dismissViewControllers() {

    guard let vc = self.presentingViewController else { return }

    while (vc.presentingViewController != nil) {
        vc.dismiss(animated: true, completion: nil)
    }
}

它将关闭所有视图控制器,直到它有一个presentingController。

编辑:如果您想要关闭/弹出已推入的视图控制器,可以使用

self.navigationController?.popToRootViewController(animated: true)

希望这有所帮助。


3
谢谢。对于所有呈现的视图控制器,它都完全有效,但对于推送的视图控制器(如果根视图控制器是导航控制器),它不起作用。所以我结合了其他人的意见,使用popToRootViewController并解决了这个问题 :) - Byoth
@Byoth 编辑了答案。 - Agent Smith
如果我想呈现视图控制器直到特定计数,例如计数为5,则应该从for循环中呈现相同的视图控制器5次。这是否可能? - Zahurafzal Mirza
@ZahurafzalMirza,您是想在弹出5次后呈现一个视图控制器,还是想呈现5个相同的视图控制器? - Agent Smith
不,我想要展示一个视图控制器5次。我已经编写了代码,但只有最后一个被呈现了。你能给我提供一下如何做的概述吗? - Zahurafzal Mirza
显示剩余4条评论

42

如果你正在使用导航,你可以使用第一个选项。如果你是以模态方式呈现,你可以使用第二个选项:

对于导航

self.navigationController?.popToRootViewController(animated: true)

用于模态展示

self.view.window!.rootViewController?.dismissViewControllerAnimated(false, completion: nil)

Erhan Demirci 我的,请亲爱的。 - Sandip Gill
Swift 3.0 用于模态呈现 self.view.window!.rootViewController?.dismiss(animated: true, completion: nil) - Nigel Brown
在“以模态方式呈现”后面的窗口上加感叹号会导致我的应用程序崩溃。所以我执行了self.view.window?.rootViewController?.dismissViewControllerAnimated(false, completion: nil)。 - Markinson
@Markinson 是的,'!'和'?'都用于定义变量是否可以为nil。如果你将其声明为'!',那么变量就不能为nil,如果它是nil,那么就会导致崩溃。'?'可以防止应用程序崩溃。 - Sandip Gill

35

大家好,这里是 Swift-4 的答案。

如果要返回根视图控制器,您只需调用一行代码即可完成工作。

 self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)

如果你有启动画面,然后是登录画面,如果你想要进入登录画面,你可以在上述代码中简单地附加 presentedviewcontroller

self.view.window?.rootViewController?.presentedViewController!.dismiss(animated: true, completion: nil)

26

如果正在呈现视图控制器,只需请求您的rootViewController将其关闭即可。

if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
   appDelegate.window?.rootViewController?.dismiss(animated: true, completion: nil)
   (appDelegate.window?.rootViewController as? UINavigationController)?.popToRootViewController(animated: true)
}

2
谢谢。对于所有呈现的视图控制器,它完全有效,但对于推送的视图控制器(如果根视图控制器是导航控制器),则无效。因此,我还反映了其他人的意见,使用popToRootViewController解决了这个问题 :) - Byoth
1
为您编辑答案。它将首先关闭所有视图控制器,然后如果根视图是 UINavigationController,则弹出所有视图控制器。 - Syed Qamar Abbas
当我没有 self 引用当前呈现窗口时,这对我起作用 - somenickname

10

根据您的视图控制器的层叠情况,返回到初始视图控制器的策略可能会有所不同。

可能会有多种情况,根据您的情况,您可以决定哪种方法是最好的。

情况1

  • 导航控制器设置为根视图控制器
  • 导航控制器将视图控制器A设置为根
  • 导航控制器推出视图控制器B
  • 导航控制器推出视图控制器C

这是一个简单的情况,在任何视图控制器中使用navigationController?.popToRootViewController(animated:true)都能使你回到视图控制器A

情况2

  • 导航控制器设置为根视图控制器
  • 导航控制器将视图控制器A设置为根
  • 视图控制器A呈现视图控制器B
  • 视图控制器B呈现视图控制器C

这种情况可以通过上面的答案来解决self?.view.window?.rootViewController.dismiss(animated: true),这将会带你回到视图控制器A

情况3

  • 导航控制器1设置为根视图控制器
  • 导航控制器1将视图控制器A设置为根
  • 导航控制器1推出视图控制器B
  • 视图控制器B呈现导航控制器2
  • 导航控制器2将视图控制器D设置为根
  • 导航控制器2推出视图控制器E

现在想象一下,您需要从视图控制器E返回到A

使用上面的两个答案此时无法解决您的问题,因为如果导航控制器不在屏幕上,就不能弹出到根视图控制器。

您可以尝试添加计时器和监听器来关闭视图控制器,然后再弹出,这可能有效,我认为上面有一个函数dismissPopAllViewViewControllers也是这样做的-但我注意到这会导致异常行为,并出现警告Unbalanced calls to begin/end appearance transitions for

我认为您可以采取以下方法来解决此类情况:

  • 首先从导航控制器本身呈现您的模态视图控制器
  • 现在您可以更好地控制要做什么

因此,我将上面的架构更改为:

  • 导航控制器1设置为根视图控制器(相同)
  • 导航控制器1将视图控制器A设置为根(相同)
  • 导航控制器1推出视图控制器B(相同)
  • 导航控制器1呈现导航控制器2(更改)
  • 导航控制器2将视图控制器D设置为根(相同)
  • 导航控制器2推出视图
    let rootViewController = self?.view.window?.rootViewController as? UINavigationController
    
    rootViewController?.setViewControllers([rootViewController!.viewControllers.first!], 
    animated: false)
    
    rootViewController?.dismiss(animated: true, completion: nil)
    

    没有任何警告,您将被传输回视图控制器 A。

    您可以根据需求进行调整,但这是重置复杂视图控制器层次结构的概念。


6
使用以下代码来关闭已呈现的视图控制器并将导航返回到根视图控制器 Swift 4
// MARK:- Dismiss and Pop ViewControllers
func dismissPopAllViewViewControllers() {
    if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
        appDelegate.window?.rootViewController?.dismiss(animated: true, completion: nil)
        (appDelegate.window?.rootViewController as? UINavigationController)?.popToRootViewController(animated: true)
    }
}

1
此答案与以下链接的答案重复:https://dev59.com/B1YN5IYBdhLWcg3whIbz#47322468 - timbre timbre

6

Swift 5.4:

self.navigationController?.popToRootViewController(animated: true)

1
弹出堆栈上除根视图控制器之外的所有视图控制器,并更新显示。
func popToRootViewController(animated: Bool)

但如果您想要前往特定的控制器,只需使用以下函数。

func popToViewController(UIViewController, animated: Bool)

弹出视图控制器,直到指定的视图控制器在导航栈的顶部。


1
为了达到您想要的效果,请修改您的导航堆栈,然后执行popViewController操作。
let allControllers = NSMutableArray(array: navigationController!.viewControllers)
let vcCount = allControllers.count
for _ in 0 ..< vcCount - 2 {
    allControllers.removeObject(at: 1)
}
// now, allControllers[0] is root VC, allControllers[1] is presently displayed VC. write back to nav stack
navigationController!.setViewControllers(allControllers as [AnyObject] as! [UIViewController], animated: false)
// then pop root VC
navigationController!.popViewController(animated: true)

查看this以了解如何进一步操作导航栈。如果您的最顶层VC是模态的,请先在上面的代码之前将其解除。


1

创建一个回 unwind Segue(您可以在https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/UsingSegues.html中找到,版权归属于 Apple Inc.)

Unwind Segue 允许您将已经呈现的视图控制器解除。通过将按钮或其他适当的对象链接到当前视图控制器的 Exit 对象,在界面构建器中创建回 Unwind Segue。当用户点击按钮或与适当的对象交互时,UIKit 会搜索视图控制器层次结构以查找能够处理回 Unwind Segue 的对象。然后它会关闭当前视图控制器和任何中间视图控制器,以显示回 Unwind Segue 的目标。

创建回 unwind Segue:

  1. 选择应该在回 unwind Segue 结束时出现在屏幕上的视图控制器。

  2. 在您选择的视图控制器上定义一个回 unwind 操作方法。

该方法的 Swift 语法如下:

@IBAction func myUnwindAction(unwindSegue: UIStoryboardSegue)

这个方法的Objective-C语法如下所示:
- (IBAction)myUnwindAction:(UIStoryboardSegue*)unwindSegue

3. 导航到发起取消动作的视图控制器。

  1. 在应该触发取消过渡的按钮(或其他对象)上按住 Control 键并单击。此元素应位于您想要关闭的视图控制器中。

  2. 将其拖动到视图控制器场景顶部的 Exit 对象上。

https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/Art/segue_unwind_linking_2x.png

  1. 从关系面板中选择您的取消操作方法。

在尝试在Interface Builder中创建相应的取消转场之前,您必须在其中一个视图控制器中定义取消操作方法。该方法的存在是必需的,并告诉Interface Builder有一个有效的目标可用于取消转场。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接