63

I have found many ways to pop back 2 UIViewControllers in UINavigationController using Objective-C, however when I try and switch that over to Swift it doesn't seem to be working.

What would be the best approach to pop back to UIViewController? Any guidance would be appreciated Thanks

ThomasW
  • 16,981
  • 4
  • 79
  • 106
candidaMan
  • 791
  • 1
  • 7
  • 12
  • Very much the same as in ObjC: Copy the `UINavigationController`'s `viewControllers` array, remove the last two elements from this array and set `viewControllers` to the modified array. There's also a `setViewControllers(_:animated:)` to include the pop animation. – fluidsonic Oct 01 '14 at 01:20
  • 3
    Alternatively you could find the second last view controller in the `viewControllers` array and then use `popToViewController` to avoid overwriting the entire view controller stack. – Aaron Wojnowski Oct 01 '14 at 01:24
  • You can pop to a controller by his class http://stackoverflow.com/questions/30003814/how-can-i-pop-specific-view-controller-in-swift/40314079#40314079 – PabloR Oct 28 '16 at 22:40

9 Answers9

175

Expanding on my comment, find the second last view controller in the viewControllers array and then use popToViewController to avoid overwriting the entire view controller stack.

Example (assumes the navigation controller has more than 1 view controller):

func backTwo() {
    let viewControllers: [UIViewController] = self.navigationController!.viewControllers as [UIViewController]
    self.navigationController!.popToViewController(viewControllers[viewControllers.count - 3], animated: true)
}

Objective-C

NSArray *viewControllers = [self.navigationController viewControllers];
[self.navigationController popToViewController:viewControllers[viewControllers.count - 3] animated:YES];
Sunil Targe
  • 7,251
  • 5
  • 49
  • 80
Aaron Wojnowski
  • 6,352
  • 5
  • 29
  • 46
  • this worked wonderfully thanks! Followup question: When it does pop to the correct view controller, it first turns the landscape View to Portrait, and then pops. Is there a way to not have it rotate the landscape view before popping? – candidaMan Oct 02 '14 at 16:00
  • 7
    wow thank you Aaron, this worked perfectly! strangely enough, I had to put `viewControllers.count - 3` to pop back two steps, but working wonderfully now! – Camillo Feb 01 '15 at 14:57
  • viewControllers array includes the current UIViewController too, hence you have to use viewControllers.count - 3 – haris Mar 11 '18 at 09:30
34

I wrote an UIViewController extension (Swift 3+ ready)

You could use like this :

/// pop back n viewcontroller
func popBack(_ nb: Int) {
    if let viewControllers: [UIViewController] = self.navigationController?.viewControllers {
        guard viewControllers.count < nb else {
            self.navigationController?.popToViewController(viewControllers[viewControllers.count - nb], animated: true)
            return
        }
    }
}

Usage :

self.popBack(3)

Bonus dismiss to a specific viewcontroller

/// pop back to specific viewcontroller
func popBack<T: UIViewController>(toControllerType: T.Type) {
    if var viewControllers: [UIViewController] = self.navigationController?.viewControllers {
        viewControllers = viewControllers.reversed()
        for currentViewController in viewControllers {
            if currentViewController .isKind(of: toControllerType) {
                self.navigationController?.popToViewController(currentViewController, animated: true)
                break
            }
        }
    }
}

Usage :

self.popBack(toControllerType: MyViewController.self)
Maximelc
  • 2,384
  • 1
  • 21
  • 17
13

user5320485 answer in swift3

let viewControllers = self.navigationController!.viewControllers as [UIViewController];
for aViewController:UIViewController in viewControllers {
    if aViewController.isKind(of: AdCreateViewController.self) {
        _ = self.navigationController?.popToViewController(aViewController, animated: true)
    }
}
Nidhin
  • 628
  • 7
  • 16
11

You can dynamically pop multiple viewcontrollers in (swift 2.0)

let allViewController: [UIViewController] = self.navigationController!.viewControllers as [UIViewController];

                        for aviewcontroller : UIViewController in allViewController
                        {
                            if aviewcontroller .isKindOfClass(YourDestinationViewControllerName)
                            {
                             self.navigationController?.popToViewController(aviewcontroller, animated: true)
                            }
                        }
Reema
  • 1,200
  • 10
  • 10
9

Swift 4 UINavigationController extension version of @Maximelc's answer:

extension UINavigationController {

    func popBack(_ nb: Int) {
        let viewControllers: [UIViewController] = self.viewControllers
        guard viewControllers.count < nb else {
            self.popToViewController(viewControllers[viewControllers.count - nb], animated: true)
            return
        }
    }

    /// pop back to specific viewcontroller
    func popBack<T: UIViewController>(toControllerType: T.Type) {
        var viewControllers: [UIViewController] = self.viewControllers
        viewControllers = viewControllers.reversed()
        for currentViewController in viewControllers {
            if currentViewController .isKind(of: toControllerType) {
                self.popToViewController(currentViewController, animated: true)
                break
            }
        }
    }

 }
CodenameDuchess
  • 1,221
  • 18
  • 24
  • 2
    Usage: `self.navigationController?.popBack(1) self.navigationController?.popBack(toControllerType: YourViewController.self)` – Naresh Dec 18 '20 at 11:46
3

If you want to pop on a specific view controller you can search that view controller by replacing DestinationViewController with your view controller in sample code.

func moveToDestinationViewController() {
    let viewController = self.navigationController?.viewControllers.first { $0 is DestinationViewController }
    guard let destinationVC = viewController else { return }
    self.navigationController?.popToViewController(destinationVC, animated: true)
}
Umair Ali
  • 758
  • 8
  • 17
1

Here is another, slightly "fool-proof" version:

extension UINavigationController {
    func popBack(_ count: Int) {
        guard count > 0 else {
            return assertionFailure("Count can not be a negative value.")
        }
        let index = viewControllers.count - count - 1
        guard index > 0 else {
            return assertionFailure("Not enough View Controllers on the navigation stack.")
        }
        popToViewController(viewControllers[index], animated: true)
    }
}
Balázs Vincze
  • 3,550
  • 5
  • 29
  • 60
1
 let allViewControllers : [UIViewController] = self.navigationController!.viewControllers as [UIViewController]
        self.navigationController?.popToViewController(allViewControllers[allViewControllers.count-3], animated: true)
Davender Verma
  • 503
  • 2
  • 12
0

I did a helper to make it easy:

import Foundation
import UIKit

class NavigationHelper {

    static func back(number: Int, from viewControlller : UIViewController) {
        let viewsBack = number + 1
        let viewControllers: [UIViewController] = viewControlller.navigationController!.viewControllers as [UIViewController]
        viewControlller.navigationController!.popToViewController(viewControllers[viewControllers.count - viewsBack], animated: true)
    }

    static func back(number: Int, from viewControlller: UITableViewController) {
        let viewsBack = number + 1
        let viewControllers: [UIViewController] = viewControlller.navigationController!.viewControllers as [UIViewController]
        viewControlller.navigationController!.popToViewController(viewControllers[viewControllers.count - viewsBack], animated: true)
    }
}

For example, in your ViewController you can use NavigationHelper.back(number: 1, from: self)

Andres Paladines
  • 1,142
  • 14
  • 19