从导航堆栈中删除视图控制器


Answers:


167

使用此代码并享受:

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];

希望这会帮助你。

编辑:Swift代码

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

我已经绑这个,不起作用。有人告诉我,与属性有关的原因导致它没有取消分配视图控制器。
Noah Passalacqua 2014年

1
这个曾在iOS版<7,但会导致奇怪的行为在iOS的7
本^ h

1
非常适合iOS 8!
Evan R

4
Vivek:告诉我您尝试过什么,并且有礼貌地在否决之前思考。
Nitin

7
此方法确实从堆栈中删除了一个viewcontroller,但似乎也没有一个NavigationItems堆栈受到影响。我在ios 8.4中得到的行为是这样的:说我们有控制器1 2 3 45。我删除了4,显示在5上的后退按钮不受影响。我单击返回,它显示3,但标题为4。再次单击,它显示3,标题为3
Radu Simionescu

49

您可以首先获取数组中的所有视图控制器,然后在检查了相应的视图控制器类之后,可以删除所需的视图控制器类。

这是一小段代码:

NSArray* tempVCA = [self.navigationController viewControllers];

for(UIViewController *tempVC in tempVCA)
{
    if([tempVC isKindOfClass:[urViewControllerClass class]])
    {
        [tempVC removeFromParentViewController];
    }
}

我认为这将使您的工作更加轻松。


这可以用于多种用途。谢谢:)
Hemang 2013年

10
当我使用该控制器时,将其正确卸下。但是,当我使用“后退”按钮时,导航栏会显示已删除的viewController的信息。其他人会收到这种奇怪的行为吗,我该如何解决?
罗宾·埃勒克曼

1
@Robin Ellerkmann您找到解决该问题的方法了吗?我正在删除ViewController,但后退按钮仍保留在导航栏中。
Mehmet Emre

2
@MehmetEmre我将Swift 2.1与self.navigationController?.viewControllers.removeLast()一起使用。这对我来说很好。
罗宾·埃勒克曼

1
当我进入4时,注销所有viewcontroller时,viewcontroller的内存为80MB。内存仍为80MB。因此内存不会释放。:(
Anil Gupta

39

斯威夫特3&4/5

self.navigationController!.viewControllers.removeAll()

self.navigationController?.viewControllers.remove(at: "insert here a number")

斯威夫特2.1

移除所有:

self.navigationController!.viewControllers.removeAll()

从索引处删除

self.navigationController?.viewControllers.removeAtIndex("insert here a number")

还有很多其他可能的动作,例如removeFirst,range等。


3
查看您的答案,我对项目的工作流程有所了解。非常感谢。
Anirudha Mahale

这会自行删除NavigationController,而不是清理视图控制器的堆栈
Daniel Beltrami

16

斯威夫特5:

navigationController?.viewControllers.removeAll(where: { (vc) -> Bool in
    if vc.isKind(of: MyViewController.self) || vc.isKind(of: MyViewController2.self) {
        return false
    } else {
        return true
    }
})

3
return !vc.isKind(of: MyViewController.self) && !vc.isKind(of: MyViewController2.self)会在一线工作:-)
马克

10

最好使用setViewControllersfrom函数UINavigationController。还有animated用于启用动画的参数。

func setViewControllers(_ viewControllers: [UIViewController], animated: Bool)

迅速提问的例子

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)
}

我尝试了类似的其他方式[tempVC removeFromParentViewController];。它产生了怪异的行为,删除了ViewController导航,当弹出时仍会显示,如@ robin-ellerkmann所报告的那样


5
这实际上是最好的解决方案:从VC中删除VC,然后使用setViewController分配新的数组。我还检查了僵尸或参考周期,这很安全。
OhadM

我确认这是一个很好的解决方案:实际上,我setViewControllers(_:animated:)在两种方式上都使用了该技术:弹出多个控制器并推送多个控制器。
心教堂

8

Swift 2.0:

  var navArray:Array = (self.navigationController?.viewControllers)!
  navArray.removeAtIndex(navArray.count-2)
  self.navigationController?.viewControllers = navArray

2
因此,您不必强行打开导航控制器的包装,可以将其if var navArray = ... { ... }
设为

6

Swift 5,Xcode 11.3

通过指定要从导航堆栈中删除的视图控制器,我发现这种方法很简单。

extension UINavigationController {

    func removeViewController(_ controller: UIViewController.Type) {
        if let viewController = viewControllers.first(where: { $0.isKind(of: controller.self) }) {
            viewController.removeFromParent()
        }
    }
}

使用示例:

navigationController.removeViewController(YourViewController.self)

5

如果您试图从第5个视图控制器移至第2个视图控制器(跳过第3和第4个),则想使用 [self.navigationController popToviewController:secondViewController]

您可以secondViewController从导航控制器堆栈中获取。

secondViewController =  [self.navigationController.viewControllers objectAtIndex:yourViewControllerIndex];

1
不想弹出当前的viewcontroller。当前的视图控制器应保持不变。但是我需要弹出位于堆栈下方的2个视图控制器
Jean Paul Scott

@JeanPaulScott。我不知道您为什么要这样做(如果不是为了弹出窗口呢?!)。
Vignesh 2012年

在某些情况下,我会将同一个ViewController的不同实例推入堆栈。因此,当创建一个新实例并将其推入堆栈时,我想弹出前一个实例以及与之关联的viewcontroller。
Jean Paul Scott

@Vignesh由于“滑动弹出”手势,这将无法在iOS 7中正常工作
Dennis

@JeanPaulScott实现您想要的目标,最安全的方法是在推送新的视图控制器实例之前弹出两次。
Radu Simionescu

4

用这个

if let navVCsCount = navigationController?.viewControllers.count {
    navigationController?.viewControllers.removeSubrange(Range(2..<navVCsCount - 1))
}

它将负责navigationController的ViewController。viewControllers以及堆叠在navigationBar中的navigationItems。

注意:请确保至少在viewDidAppear之后调用它


1
此方法在Swift 5,Xcode 10.3中对我来说非常有效...如果让navVCsCount = navigationController?.viewControllers.count {self.navigationController?.viewControllers.removeSubrange(navVCsCount-3 .. <navVCsCount-1)}
Kedar Sukerkar

2

此解决方案迅速为我工作:

let VCCount = self.navigationController!.viewControllers.count
self.navigationController?.viewControllers.removeSubrange(Range(VCCount-3..<VCCount - 1))

您当前在堆栈中的视图控制器索引为:

self.navigationController!.viewControllers.count - 1

2

Swift 5.1,Xcode 11

extension UINavigationController{
public func removePreviousController(total: Int){
    let totalViewControllers = self.viewControllers.count
    self.viewControllers.removeSubrange(totalViewControllers-total..<totalViewControllers - 1)
}}

确保在先前控制器的viewDidDisappear()或新控制器的viewDidAppear()之后调用此实用程序函数


1

细节

  • Swift 5.1,Xcode 11.3.1

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()!)
    }
}

用法

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 }

完整样本

不要忘记将解决方案代码粘贴到此处

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 }
    }
}

结果

在此处输入图片说明


0

我编写了一个方法扩展,除非另有说明,否则该方法将删除root和top之间的所有控制器。

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)
}

}

如果要使用范围(例如2到5),则可以使用

    let range = 2 ..< 5
    viewControllers.removeSubrange(range)

在iOS 12.2,Swift 5上测试


0

//从堆栈中按类名删除viewcontrollers,然后关闭当前视图。

 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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.