52

I have three controllers (FirstVC, SecondVC, ThirdVC) inside storyboad, and navigation is sequential: a user can navigate from FirstVC to SecondVC, and then to ThirdVC. Now, I need to make some button that will open ThirdVC from FirstVC but will also put SecondVC on navigation stack, so when a user will press back from ThirdVC he will be returned to SecondVC. So, I don’t need animation from FirstVC to SecondVC, just need to push SecondVC on navigation controller stack and then animate only transition to ThirdVC.

I was unable to find how disable animation for performSegueWithIdentifier, so I’m thinking I should instantiate SecondVC from storyboard manually, put it on navigation stack, and then perform performSegueWithIdentifier for ThirdVC. Any ideas how to do that?

Ortwin Gentz
  • 48,409
  • 23
  • 127
  • 204
Centurion
  • 13,168
  • 31
  • 99
  • 189
  • Have a look here on how to replace (or add) a view controller in the navigation controller history: http://stackoverflow.com/questions/8627050/replace-a-uiviewcontroller-in-the-uinavigationcontroller-hierarchy – TheEye Feb 18 '14 at 14:51
  • 1
    You answered your own question ! Push your SecondVC without animation on your navigationController then perform your segue as usually from SecondVC. – Francescu Feb 18 '14 at 15:15

7 Answers7

88

The solution you're looking for if you're in the firstVC:

NSMutableArray *controllers = [self.navigationController.viewControllers mutableCopy];
[controllers addObject:secondVc];
[controllers addObject:thirdVC];
[self.navigationController setViewControllers:controllers animated:YES];

This will animate in the thirdVC without the secondVc becoming visible in the process. When the user press the back button, they will return to the secondVc

KimHafr
  • 2,854
  • 3
  • 24
  • 25
  • 2
    I liked this solution, I had a different scenario where I needed to maintain the current stack, I accomplished this by accessing the current stack and appending the new controllers before calling the setViewControllers method. (swift) ```var controllers = self.navigationController?.viewControllers``` – Blakedallen May 20 '15 at 07:21
26

To preserve the standard animation for pushing a view controller, in Swift:

let pushVC = UIViewController()
let backVC = UIViewController()

if let navigationController = navigationController {

  navigationController.pushViewController(pushVC, animated: true)

  let stackCount = navigationController.viewControllers.count
  let addIndex = stackCount - 1
  navigationController.viewControllers.insert(backVC, atIndex: addIndex)

}

This displays pushVC normally and inserts backVC into the navigation stack, preserving both the animation and the history for the UINavigationController.

You can use setViewControllers, but you'll lose the standard push animation.

David Smith
  • 326
  • 4
  • 5
14

Swift Version From KimAMartinsen Solution

guard var controllers = self.navigationController?.viewControllers else {
   return
}

guard let firstVC = self.storyboard?.instantiateViewController(withIdentifier: "Tests") as? firstViewController else { 
   return
}

guard let secondVC = self.storyboard?.instantiateViewController(withIdentifier: "Dashboard") as? SecondViewController else {
    return
}

controllers.append(firstVC)
controllers.append(secondVC)
self.navigationController?.setViewControllers(controllers, animated: true)
Pranav Kasetti
  • 4,773
  • 2
  • 16
  • 38
Salem Binmusaed
  • 815
  • 7
  • 8
10
extension UINavigationController {
    open func pushViewControllers(_ inViewControllers: [UIViewController], animated: Bool) {
        var stack = self.viewControllers
        stack.append(contentsOf: inViewControllers)
        self.setViewControllers(stack, animated: animated)
    }
}
Ryan Brodie
  • 6,090
  • 7
  • 37
  • 57
Al Zonke
  • 472
  • 5
  • 8
2

Hmm perhaps just write a custom segue that doesn't do any animations an make sure the segue in the storyboard references your segue class.

https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/CreatingCustomSegues/CreatingCustomSegues.html

Link above to how to create it, docs IMO are fairly self explanatory. You can then customise how you segues work and appear.

Other options include the new protocols introduced in iOS7 assuming you don't want to support older devices.

Watch the Apple Tech Talks 2014 video "Architecting Modern Apps part 1" they demo it on there.

https://developer.apple.com/tech-talks/videos/

There are many solutions to your question hopefully one of the above is helpful, let me know if it's not and I'll propose another.

UPDATE:

Another option would be to use a tav view controller perhaps if this fits in with your needs as you could add a navigation controller to one of the tabs to achieve this or swap tabs as needed.

AppHandwerker
  • 1,641
  • 12
  • 22
0

I suggest pushing your view controllers manually. The first without animation:

[self.navigationController pushViewController:SecondVC animated:NO];
[self.navigationController pushViewController:ThirdVC animated:YES];
Ortwin Gentz
  • 48,409
  • 23
  • 127
  • 204
0

I use the following snippet to push multiple View Controllers:

extension UINavigationController {
    func push(_ viewControllers: [UIViewController]) {
        setViewControllers(self.viewControllers + viewControllers, animated: true)
    }

    // Also had this in here, left it in as a bonus :)
    func popViewControllers(_ count: Int) {
        guard viewControllers.count > count else { return }
        popToViewController(viewControllers[viewControllers.count - count - 1], animated: true)
    }
}
Balázs Vincze
  • 2,903
  • 2
  • 24
  • 52