22

I am trying to pop to the root view controller using the following code:

self.navigationController!.popToRootViewController(animated: true)

This usually works, but I get an error when trying to use this code when the current view is a modal. How do I go about popping back to the root view controller in this situation?

Thanks in advance.

user1391152
  • 1,259
  • 3
  • 13
  • 28

7 Answers7

33

You can check that current controller is presented, if it is presented then dismiss it and the go to the rootViewController other wise go directly the rootViewController

if self.presentingViewController != nil {
    self.dismiss(animated: false, completion: { 
       self.navigationController!.popToRootViewController(animated: true)
    })
}
else {
    self.navigationController!.popToRootViewController(animated: true)
}
Nirav D
  • 71,513
  • 12
  • 161
  • 183
  • 4
    Looks like the `self.navigationController!.popToRootViewController(animated: true)` in the completion handler may not be correct? Shouldn't you be using the `presentingViewController`'s navigation controller to pop to root? – Yuchen Apr 16 '18 at 14:45
16

Result:

Code

Let's say that your Modal View has the below ViewController associated.

Basically first to hide your View that is shown as a Modal, you use dismiss(animated: Bool) method from your ViewController instance.

And for the Views presented as Pushed, you could use from your navigationController property these methods for instance: popToRootViewController(animated: Bool), popViewController(animated:Bool)

class ModalViewController: UIViewController {
  
  @IBAction func backButtonTouched(_ sender: AnyObject) {
    let navigationController = self.presentingViewController as? UINavigationController
    
    self.dismiss(animated: true) {
      let _ = navigationController?.popToRootViewController(animated: true)
    }
  }
  
}
Community
  • 1
  • 1
Wilson
  • 9,006
  • 3
  • 42
  • 46
3

You have presented a ViewController, so you need to dismiss it.

To dismiss a ViewController in swift, use this:

self.dismiss(animated: true, completion: nil)
KSR
  • 1,699
  • 15
  • 22
1

If you don't want the animations to happen, i.e. you want user to see the root view controller after the modal view is dismissed:

CATransaction.begin()
CATransaction.setCompletionBlock {
    self.dismiss(animated: true, completion: nil)
}
self.navigationController?.popViewController(animated: false)
CATransaction.commit()

Reference from: Completion block for popViewController

The above will work for popping a single view controller. If you need to pop multiple, popToRootViewController will not work (there is an unbalanced call due to animations).

In that case, use the following (manual) method to remove them:

guard let navigationController = self.navigationController else { return }
var navigationViewControllers = navigationController.viewControllers
navigationViewControllers.removeLast(navigationViewControllers.count - 1)
self.navigationController?.viewControllers = navigationViewControllers
CyberMew
  • 1,159
  • 1
  • 16
  • 33
0

You can do something like this:

Objective-C:

[(UINavigationController *)self.presentingViewController popToRootViewControllerAnimated:NO];

[self dismissViewControllerAnimated:YES completion:nil];

Check answer here for reference:

ios: how to dismiss a modal view controller and then pop a pushed view controller

Community
  • 1
  • 1
Ronak Chaniyara
  • 5,335
  • 3
  • 24
  • 51
0

How about use Notification?

your modal just post Notification

and your NavigationController receive then, pop to rootViewController

Cruz
  • 2,602
  • 19
  • 29
0

I created a project with a custom navigation bar so that the root back button can be in the navigation bar. The green arrow goes back one View, while the blue x will pop back to the root view which is my case is the ContentView.

enter image description here

Here are my example views to show how it works.

struct ContentView: View {
    @EnvironmentObject private var globalObj: GlobalClass
    var body: some View {
        CustomNavView{
            VStack{
                RootCustomNavLink(destination: FirstView()
                .customNavigationTitle("Custom View").customNavigationBarReturnButtonHidden(true), label: {
                        Text("Navigate View One").buttonStyleBlue()
                    })
                }.customNavigationTitle("Home")
                    .customNavigationBarBackButtonHidden(true)
                    .customNavigationBarReturnButtonHidden(true)
            }
        }
    }
struct FirstView: View {
    var body: some View {
        Text("Hello, View One!")
        CustomNavLink(destination: LastView()
        .customNavigationTitle("Last View").customNavigationSubtitle("subtitle"), label: {
            Text("Go to Last View").buttonStyleGreen()
        })
    }
}

struct LastView: View {
    var body: some View {
        Text("Last View")
    }
}

Here is my GitHub repo that has the structs that I made to build this custom navigation view.

https://github.com/MicahKimel/CustomNavigationSwiftUI

micah
  • 838
  • 7
  • 21