I have a navigation stack, with say 5 UIViewControllers. I want to remove the 3rd and 4th viewcontrollers in the stack on the click of a button in the 5th viewcontroller. Is it possible to do this? If so how?
16 Answers
Use this code and enjoy:
NSMutableArray *navigationArray = [[NSMutableArray alloc] initWithArray: self.navigationController.viewControllers];
// [navigationArray removeAllObjects]; // This is just for remove all view controller from navigation stack.
[navigationArray removeObjectAtIndex: 2]; // You can pass your index here
self.navigationController.viewControllers = navigationArray;
[navigationArray release];
Hope this will help you.
Edit: Swift Code
guard let navigationController = self.navigationController else { return }
var navigationArray = navigationController.viewControllers // To get all UIViewController stack as Array
navigationArray.remove(at: navigationArray.count - 2) // To remove previous UIViewController
self.navigationController?.viewControllers = navigationArray
Edit: To remove all ViewController except last one -> no Back Button in the upper left corner
guard let navigationController = self.navigationController else { return }
var navigationArray = navigationController.viewControllers // To get all UIViewController stack as Array
let temp = navigationArray.last
navigationArray.removeAll()
navigationArray.append(temp!) //To remove all previous UIViewController except the last one
self.navigationController?.viewControllers = navigationArray
-
i have tied this and does not work. i was told that something to do with the properties is causing it to not dealloc the viewcontrollers. – Noah Passalacqua Mar 18 '14 at 14:35
-
1this worked in iOS < 7, but results in strange behavior in iOS 7. – Ben H Apr 22 '14 at 23:30
-
I will add code for ios7 soon... Let me check with iOS 7 – Nitin Apr 23 '14 at 02:15
-
1Works great for iOS 8! – Evan R Nov 25 '14 at 01:24
-
This works good for iOS 8 but in iOS 7 it causes problem...help me if you have any solution – Vivek Shah Mar 27 '15 at 07:07
-
4Vivek : Show me what have you tried and have a courtesy to think before negative vote. – Nitin Mar 27 '15 at 09:23
-
9this method removes a viewcontroller indeed from the stack but there also seems to be a navigationitems stack which doesn't get affected. The behavior I get in ios 8.4 is like this: say we have controllers 1 2 3 4 5. I remove 4, the back button showed on 5 is not affected. I click back, it shows 3 but the title of 4. I click back again, it shows 3 with the title of 3 – Radu Simionescu Sep 15 '15 at 09:30
-
Swift equivalent anybody? – Nagendra Rao Oct 06 '15 at 06:48
-
I use this code on iOS 9 and it works greatly. Thanks! – Chen Li Yong Feb 19 '16 at 06:36
-
this should work unless you are doing something funny with data passing via properties and delegates – Ashish P Mar 06 '17 at 06:54
You can first get all the view controllers in the array and then after checking with the corresponding view controller class, you can delete the one you want.
Here is small piece of code:
NSArray* tempVCA = [self.navigationController viewControllers];
for(UIViewController *tempVC in tempVCA)
{
if([tempVC isKindOfClass:[urViewControllerClass class]])
{
[tempVC removeFromParentViewController];
}
}
I think this will make your work easier.

- 35,397
- 25
- 123
- 176

- 2,524
- 1
- 17
- 19
-
-
10When I use this the controller is removed properly. But when I use the "Back" button my navigation bar shows the information of the removed viewController. Does anybody else receive this weird behavior and how can I fix it? – Robin Ellerkmann Sep 08 '15 at 07:32
-
Removing a the viewcontroller from parentviewcontroller? It seemed dodgy to me so I tested on iOS 9 and it simply removes view of tempVC from itself and returns it to its default state as in storyboard. Thats about it – NSNoob Dec 03 '15 at 09:25
-
1@Robin Ellerkmann did you find solution for that problem? i am removing viewcontroller but back button remains at navigation bar. – Mehmet Emre Portakal Feb 26 '16 at 15:38
-
2@MehmetEmre I use Swift 2.1 with self.navigationController?.viewControllers.removeLast(). This works pretty good for me. – Robin Ellerkmann Feb 26 '16 at 15:51
-
-
2When I was in 4 viewcontroller memory was 80MB when log out all viewcontroller get removed. Memory still 80MB. So memory is not releasing. :( – Anil Gupta Mar 09 '17 at 14:45
-
My back button stops working.....I have NavController, vc1, vc2, vc3. If I'm on vc3 and do this strategy to remove vc2, now my back button won't work. I was hoping I could just press it to go back to vc1 but it doesn't do anything... – Eric Mar 08 '22 at 02:47
Swift 3 & 4/5
self.navigationController!.viewControllers.removeAll()
self.navigationController?.viewControllers.remove(at: "insert here a number")
Swift 2.1
remove all:
self.navigationController!.viewControllers.removeAll()
remove at index
self.navigationController?.viewControllers.removeAtIndex("insert here a number")
There a bunch of more possible actions like removeFirst,range etc.

- 7,124
- 1
- 51
- 69
-
3Looking at your answer, I got an idea for my project's workflow. Thanks a lot. – Anirudha Mahale Jun 02 '17 at 08:08
-
This remove the NavigationController it self, not clean a stack of view controllers – Daniel Beltrami Aug 30 '18 at 20:00
Swift 5:
navigationController?.viewControllers.removeAll(where: { (vc) -> Bool in
if vc.isKind(of: MyViewController.self) || vc.isKind(of: MyViewController2.self) {
return false
} else {
return true
}
})
-
8`return !vc.isKind(of: MyViewController.self) && !vc.isKind(of: MyViewController2.self)` would do the job in one line :-) – Mark Jun 20 '19 at 13:00
Swift 5, Xcode 13
I found this approach simple by specifying which view controller(s) you want to remove from the navigation stack.
extension UINavigationController {
func removeViewController(_ controller: UIViewController.Type) {
if let viewController = viewControllers.first(where: { $0.isKind(of: controller.self) }) {
viewController.removeFromParent()
}
}
}
Example use:
navigationController.removeViewController(YourViewController.self)

- 309
- 3
- 9
Using setViewControllers
function from UINavigationController
is the best way. There is also animated
parameter to enable animation.
func setViewControllers(_ viewControllers: [UIViewController], animated: Bool)
Example in swift for question
func goToFifthVC() {
var currentVCStack = self.navigationController?.viewControllers
currentVCStack?.removeSubrange(2...3)
let fifthVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "fifthVC")
currentVCStack?.append(fifthVC)
self.navigationController?.setViewControllers(currentVCStack!, animated: true)
}
I tried other ways like [tempVC removeFromParentViewController];
. It make weird behaviour, removed ViewController navigation still showing when pop back like reported by @robin-ellerkmann

- 3,940
- 2
- 30
- 34
-
5This is actually the best solution: removing the VC from the navigationController?.viewControllers array and using setViewControllers to assign the new array. I've also checked for zombies or reference cycles, it's safe. – OhadM Nov 04 '18 at 12:40
-
I confirm that it's an excellent solution: I'm actually using that `setViewControllers(_:animated:)` technique in both ways: to pop multiple controllers and to push multiple controllers. – Cœur Feb 03 '19 at 17:06
Swift 2.0:
var navArray:Array = (self.navigationController?.viewControllers)!
navArray.removeAtIndex(navArray.count-2)
self.navigationController?.viewControllers = navArray

- 89
- 1
- 4
-
2So you're not force unwrapping the navigation controller, you could make it an if statement `if var navArray = ... { ... }` – Kiley Aug 11 '16 at 15:11
Details
- Swift 5.1, Xcode 11.3.1
Solution
extension UIViewController {
func removeFromNavigationController() { navigationController?.removeController(.last) { self == $0 } }
}
extension UINavigationController {
enum ViewControllerPosition { case first, last }
enum ViewControllersGroupPosition { case first, last, all }
func removeController(_ position: ViewControllerPosition, animated: Bool = true,
where closure: (UIViewController) -> Bool) {
var index: Int?
switch position {
case .first: index = viewControllers.firstIndex(where: closure)
case .last: index = viewControllers.lastIndex(where: closure)
}
if let index = index { removeControllers(animated: animated, in: Range(index...index)) }
}
func removeControllers(_ position: ViewControllersGroupPosition, animated: Bool = true,
where closure: (UIViewController) -> Bool) {
var range: Range<Int>?
switch position {
case .first: range = viewControllers.firstRange(where: closure)
case .last:
guard let _range = viewControllers.reversed().firstRange(where: closure) else { return }
let count = viewControllers.count - 1
range = .init(uncheckedBounds: (lower: count - _range.min()!, upper: count - _range.max()!))
case .all:
let viewControllers = self.viewControllers.filter { !closure($0) }
setViewControllers(viewControllers, animated: animated)
return
}
if let range = range { removeControllers(animated: animated, in: range) }
}
func removeControllers(animated: Bool = true, in range: Range<Int>) {
var viewControllers = self.viewControllers
viewControllers.removeSubrange(range)
setViewControllers(viewControllers, animated: animated)
}
func removeControllers(animated: Bool = true, in range: ClosedRange<Int>) {
removeControllers(animated: animated, in: Range(range))
}
}
private extension Array {
func firstRange(where closure: (Element) -> Bool) -> Range<Int>? {
guard var index = firstIndex(where: closure) else { return nil }
var indexes = [Int]()
while index < count && closure(self[index]) {
indexes.append(index)
index += 1
}
if indexes.isEmpty { return nil }
return Range<Int>(indexes.min()!...indexes.max()!)
}
}
Usage
removeFromParent()
navigationController?.removeControllers(in: 1...3)
navigationController?.removeController(.first) { $0 != self }
navigationController?.removeController(.last) { $0 != self }
navigationController?.removeControllers(.all) { $0.isKind(of: ViewController.self) }
navigationController?.removeControllers(.first) { !$0.isKind(of: ViewController.self) }
navigationController?.removeControllers(.last) { $0 != self }
Full Sample
Do not forget to paste here the solution code
import UIKit
class ViewController2: ViewController {}
class ViewController: UIViewController {
private var tag: Int = 0
deinit { print("____ DEINITED: \(self), tag: \(tag)" ) }
override func viewDidLoad() {
super.viewDidLoad()
print("____ INITED: \(self)")
let stackView = UIStackView()
stackView.axis = .vertical
view.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
stackView.addArrangedSubview(createButton(text: "Push ViewController() white", selector: #selector(pushWhiteViewController)))
stackView.addArrangedSubview(createButton(text: "Push ViewController() gray", selector: #selector(pushGrayViewController)))
stackView.addArrangedSubview(createButton(text: "Push ViewController2() green", selector: #selector(pushController2)))
stackView.addArrangedSubview(createButton(text: "Push & remove previous VC", selector: #selector(pushViewControllerAndRemovePrevious)))
stackView.addArrangedSubview(createButton(text: "Remove first gray VC", selector: #selector(dropFirstGrayViewController)))
stackView.addArrangedSubview(createButton(text: "Remove last gray VC", selector: #selector(dropLastGrayViewController)))
stackView.addArrangedSubview(createButton(text: "Remove all gray VCs", selector: #selector(removeAllGrayViewControllers)))
stackView.addArrangedSubview(createButton(text: "Remove all VCs exept Last", selector: #selector(removeAllViewControllersExeptLast)))
stackView.addArrangedSubview(createButton(text: "Remove all exept first and last VCs", selector: #selector(removeAllViewControllersExeptFirstAndLast)))
stackView.addArrangedSubview(createButton(text: "Remove all ViewController2()", selector: #selector(removeAllViewControllers2)))
stackView.addArrangedSubview(createButton(text: "Remove first VCs where bg != .gray", selector: #selector(dropFirstViewControllers)))
stackView.addArrangedSubview(createButton(text: "Remove last VCs where bg == .gray", selector: #selector(dropLastViewControllers)))
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if title?.isEmpty ?? true { title = "First" }
}
private func createButton(text: String, selector: Selector) -> UIButton {
let button = UIButton()
button.setTitle(text, for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: selector, for: .touchUpInside)
return button
}
}
extension ViewController {
private func createViewController<VC: ViewController>(backgroundColor: UIColor = .white) -> VC {
let viewController = VC()
let counter = (navigationController?.viewControllers.count ?? -1 ) + 1
viewController.tag = counter
viewController.title = "Controller \(counter)"
viewController.view.backgroundColor = backgroundColor
return viewController
}
@objc func pushWhiteViewController() {
navigationController?.pushViewController(createViewController(), animated: true)
}
@objc func pushGrayViewController() {
navigationController?.pushViewController(createViewController(backgroundColor: .lightGray), animated: true)
}
@objc func pushController2() {
navigationController?.pushViewController(createViewController(backgroundColor: .green) as ViewController2, animated: true)
}
@objc func pushViewControllerAndRemovePrevious() {
navigationController?.pushViewController(createViewController(), animated: true)
removeFromNavigationController()
}
@objc func removeAllGrayViewControllers() {
navigationController?.removeControllers(.all) { $0.view.backgroundColor == .lightGray }
}
@objc func removeAllViewControllersExeptLast() {
navigationController?.removeControllers(.all) { $0 != self }
}
@objc func removeAllViewControllersExeptFirstAndLast() {
guard let navigationController = navigationController, navigationController.viewControllers.count > 1 else { return }
let lastIndex = navigationController.viewControllers.count - 1
navigationController.removeControllers(in: 1..<lastIndex)
}
@objc func removeAllViewControllers2() {
navigationController?.removeControllers(.all) { $0.isKind(of: ViewController2.self) }
}
@objc func dropFirstViewControllers() {
navigationController?.removeControllers(.first) { $0.view.backgroundColor != .lightGray }
}
@objc func dropLastViewControllers() {
navigationController?.removeControllers(.last) { $0.view.backgroundColor == .lightGray }
}
@objc func dropFirstGrayViewController() {
navigationController?.removeController(.first) { $0.view.backgroundColor == .lightGray }
}
@objc func dropLastGrayViewController() {
navigationController?.removeController(.last) { $0.view.backgroundColor == .lightGray }
}
}
Result

- 24,482
- 9
- 132
- 127
If you are trying to move to 2nd view controller from 5th view controller (skipping 3rd and 4th), you would like to use [self.navigationController popToviewController:secondViewController]
.
You can obtain the secondViewController
from the navigation controller stack.
secondViewController = [self.navigationController.viewControllers objectAtIndex:yourViewControllerIndex];

- 10,205
- 2
- 35
- 73
-
1Dont want to pop the current viewcontroller. The current viewcontroller should remain intact. But I need to pop the 2 viewcontrollers lying under it in the stack – Jean Paul Apr 23 '12 at 13:55
-
@JeanPaulScott. I wonder Why would you want to do that, if not for popping up?!. – Vignesh Apr 24 '12 at 04:27
-
There is a case where I would have different instances of the same viewcontroller being pushed into the stack. So when a new instance is created and pushed into the stack, I want to pop out the previous instance and the viewcontroller associated with that. – Jean Paul Apr 24 '12 at 05:03
-
@Vignesh This wouldn't work as required in iOS 7 because of 'swipe to pop' gesture – Dennis Pashkov Feb 11 '14 at 11:35
-
@JeanPaulScott to achieve what you want, the safest thing is to pop twice before pushing your new view controller instance. – Radu Simionescu Sep 15 '15 at 09:37
Use this
if let navVCsCount = navigationController?.viewControllers.count {
navigationController?.viewControllers.removeSubrange(Range(2..<navVCsCount - 1))
}
It will take care of ViewControllers of navigationController. viewControllers and also a navigationItems stacked in navigationBar.
Note: Be sure to call it at least after viewDidAppear

- 301
- 1
- 13
-
1This method worked perfectly for me in Swift 5, Xcode 10.3...if let navVCsCount = navigationController?.viewControllers.count { self.navigationController?.viewControllers.removeSubrange(navVCsCount-3..
– Kedar Sukerkar Aug 02 '19 at 12:43
Swift 5.1, Xcode 11
extension UINavigationController{
public func removePreviousController(total: Int){
let totalViewControllers = self.viewControllers.count
self.viewControllers.removeSubrange(totalViewControllers-total..<totalViewControllers - 1)
}}
Make sure to call this utility function after viewDidDisappear() of previous controller or viewDidAppear() of new controller

- 1,410
- 1
- 16
- 22
This solution worked for me in swift 4:
let VCCount = self.navigationController!.viewControllers.count
self.navigationController?.viewControllers.removeSubrange(Range(VCCount-3..<VCCount - 1))
your current view controller index in stack is:
self.navigationController!.viewControllers.count - 1

- 622
- 1
- 8
- 18
Swift 5.4
removing SpecificViewController
navigationController.viewControllers.removeAll { $0 is SpecificViewController }

- 129
- 2
- 10
I wrote an extension with method which removes all controllers between root and top, unless specified otherwise.
extension UINavigationController {
func removeControllers(between start: UIViewController?, end: UIViewController?) {
guard viewControllers.count > 1 else { return }
let startIndex: Int
if let start = start {
guard let index = viewControllers.index(of: start) else {
return
}
startIndex = index
} else {
startIndex = 0
}
let endIndex: Int
if let end = end {
guard let index = viewControllers.index(of: end) else {
return
}
endIndex = index
} else {
endIndex = viewControllers.count - 1
}
let range = startIndex + 1 ..< endIndex
viewControllers.removeSubrange(range)
}
}
If you want to use range (for example: 2 to 5) you can just use
let range = 2 ..< 5
viewControllers.removeSubrange(range)
Tested on iOS 12.2, Swift 5

- 1,776
- 1
- 17
- 28
// removing the viewcontrollers by class names from stack and then dismissing the current view.
self.navigationController?.viewControllers.removeAll(where: { (vc) -> Bool in
if vc.isKind(of: ViewController.self) || vc.isKind(of: ViewController2.self)
{
return true
}
else
{
return false
}
})
self.navigationController?.popViewController(animated: false)
self.dismiss(animated: true, completion: nil)

- 477
- 5
- 6
You should remove the controllers from navigation controller array and ALSO their parents
navigationController?.viewControllers.removeAll(where: { (vc) -> Bool in
if vc.isKind(of: MyViewController.self)
|| vc.isKind(of: MyViewControllerType2.self)
|| vc.isKind(of: MyViewControllerType3.self) {
/* remove from parent */
vc.removeFromParent()
return true
} else {
return false
}
})

- 1,037
- 12
- 13