1

I have a few view controllers with navigation bar and tab bar. Then I have an initial view controller with no navigation bar and tab bar. I want to go back to the initial view controller with no other view controllers in stack. Here's what I do:

// Head back to Initial View Controller.
let initialViewController = self.storyboard!.instantiateViewController(withIdentifier: "Initial")
UIApplication.shared.keyWindow?.rootViewController = initialViewController
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = initialViewController

This works perfectly. But then it doesn't move to another view controller from the initial view controller again. For example:

@IBAction func loginButton(_ sender: Any) {
    print("Login Button Tapped.")

    let storyboard = UIStoryboard(name: "SignInViewController", bundle: nil)
    let signInViewController = storyboard.instantiateViewController(withIdentifier: "SignInViewController") as! SignInViewController
    self.navigationController?.show(signInViewController, sender: nil)

}

Here, the print statement is run, but self.navigationController.show won't. How do all this work? And what's the right approach to accomplish this?

EDIT:

enter image description here

To avoid any confusion, here's my storyboard. The left most viewcontroller (pink screen) is the initial view controller I want to go back to. The right most on the same level is the view where I want to go back from.

waseefakhtar
  • 1,373
  • 2
  • 24
  • 47

3 Answers3

1

It doesn't move to another view controller, because you are setting a simple view controller in your rootViewController.

What you need is:

let initialViewController = self.storyboard!.instantiateViewController(withIdentifier: "Initial")
let navController = UINavigationController.init(rootViewController: initialViewController)

UIApplication.shared.keyWindow?.rootViewController?.present(navController, animated: true, completion: nil)

This will create a navigationviewcontroller, so when on "Initial" you should be able to perform push, present actions of another view controllers.

Pedro Pinho
  • 652
  • 3
  • 6
  • This makes a lot of sense. However, I get an error: `Terminating app due to uncaught exception 'UIViewControllerHierarchyInconsistency', reason: 'adding a root view controller as a child of view controller:'` when I try this. Why's that? – waseefakhtar Jul 19 '17 at 18:01
  • Edited @waseefakhtar – Pedro Pinho Jul 19 '17 at 18:07
  • Great! It worked the first time I tried perfectly. But when I repeat the process, I get a warning: `Attempt to present on whose view is not in the window hierarchy!` and then it won't move to the initial view. – waseefakhtar Jul 19 '17 at 18:21
  • What you mean by repeat the process? What are you trying to do? – Pedro Pinho Jul 19 '17 at 18:23
  • I move from the initial view controller to the view contollers with navigation bar and tab bar, then go back to the initial view controller from the code you suggested. From there, I try to repeat the process, but once I try moving back to the initial view controller the second time, it won't move. – waseefakhtar Jul 19 '17 at 18:27
  • I also tried using `UIApplication.shared.keyWindow?.rootViewController?.show(navController, animated: true, completion: nil)`, it behaves the same way, except that I don't even see the warning message in the console now. – waseefakhtar Jul 19 '17 at 18:54
  • Can you share your code how it looks right now? and how you push/pop view controllers? – Pedro Pinho Jul 19 '17 at 19:35
  • The code in `loginButton()` in original post is how I push view controllers, and the code you suggested is how I pop the view controller. For any confusion about navigation controller and tab bar controller, I edited the original post with a screenshot of the storyboard. – waseefakhtar Jul 19 '17 at 19:39
  • I see, it has to be the other way around, on your `loginButton()` you insert the code i give to you, on the other viewcontrollers, you just do `self.navigationController?.popViewController(animated: true)` – Pedro Pinho Jul 19 '17 at 19:42
  • Apologies. I tried it to do it the other way around, but now the `popViewController` won't work. The flow of my app is as follows: InitialViewController > ViewController1 > TabBarController > ViewController2 (One of the tabs) > InitialViewController – waseefakhtar Jul 19 '17 at 20:18
  • Ok, this flow: **InitialViewController > ViewController1** On viewController1, if you do `self.navigationController?.popViewController(animated: true)`, it doesn't work? Full flow, when you get here: **ViewController2 (One of the tabs) > InitialViewController** On viewController2, the place were you need to go to initialViewController, do the following: `self.navigationController.popToRootViewController(animated: true)` – Pedro Pinho Jul 19 '17 at 20:21
  • The `popViewController` works on neither of the flow (**InitialViewController > ViewController1** or **ViewController2 (One of the tabs) > InitialViewController**). – waseefakhtar Jul 19 '17 at 20:32
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/149645/discussion-between-ppinho-and-waseefakhtar). – Pedro Pinho Jul 19 '17 at 20:33
0

self.navigationController.popToRootViewController(animated:) is what you're looking for.

https://developer.apple.com/documentation/uikit/uinavigationcontroller/1621855-poptorootviewcontroller

EDIT

Try dismissing the navigationController after calling popToRoot. self.navigationController.dismiss(animated: true, completion: nil)

EDIT

Try the following:

@IBAction func loginButton(_ sender: Any) {
    print("Login Button Tapped.")

    let storyboard = UIStoryboard(name: "SignInViewController", bundle: nil)
    let signInViewController = storyboard.instantiateViewController(withIdentifier: "SignInViewController") as! SignInViewController
    present(signInViewController, animated: true, completion: nil)
    //self.navigationController?.show(signInViewController, sender: nil)
}

Then to dismiss the signInViewController call self?.navigationController.dismiss(animated:true, completion:nil) from inside the signInViewController.

Harry Singh
  • 826
  • 5
  • 11
  • No; this just takes the user back to the first VC in the navigation stack, not the initial VC that appears before the navigation stack. – NRitH Jul 19 '17 at 17:38
  • try the updated answer which is dismiss the navigationController all together. – Harry Singh Jul 19 '17 at 17:43
  • That's what my answer says, except that you don't have to bother with popping to the root before you dismiss it. – NRitH Jul 19 '17 at 17:47
  • @NRitH is right. I've tried this before. But it won't work, it pops the current viewcontroller to the initial viewcontroller within the tab bar, so the tab bar and navigation bar still shows. – waseefakhtar Jul 19 '17 at 18:05
  • No, it did not. – waseefakhtar Jul 19 '17 at 18:53
  • Can you try calling dismiss on the `navigationController` from the `controller` that `presents` the navigation stack? – Harry Singh Jul 19 '17 at 18:54
  • I try `self.navigationController?.dismiss(animated: true, completion: nil)` but it won't move back to the initial view. Though I don't know what the navigationController dismisses with the method. The initial view I have is not part of the navigationController or tabBarController of the view controller I try to go back from. It's not part of the navigation stack. – waseefakhtar Jul 19 '17 at 19:20
  • Also, for any confusion about navigation controller and tab bar controller, I edited the original post with a screenshot of the storyboard. – waseefakhtar Jul 19 '17 at 19:40
0

From your initial view controller, you need to instantiate your navigation stack and present it modally. Then, when you want to get back to your pre-navigation initial VC, just call dismiss(animated:,completion:), which will dismiss your entire navigation stack and return you to the initial view controller.

Harry Singh
  • 826
  • 5
  • 11
NRitH
  • 13,441
  • 4
  • 41
  • 44