Xóa bộ điều khiển chế độ xem khỏi ngăn xếp điều hướng


92

Tôi có một ngăn xếp điều hướng, với 5 UIViewControllers. Tôi muốn xóa bộ điều khiển chế độ xem thứ 3 và thứ 4 trong ngăn xếp bằng cách nhấp vào nút trong bộ điều khiển chế độ xem thứ 5. có khả năng làm cái này không? Nếu vậy thì làm thế nào?

Câu trả lời:


167

Sử dụng mã này và tận hưởng:

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

Hy vọng điều này sẽ giúp bạn.

Chỉnh sửa: Mã 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

tôi đã buộc nó và không hoạt động. tôi đã được thông báo rằng điều gì đó liên quan đến các thuộc tính khiến nó không xử lý được các bộ điều khiển chế độ xem.
Noah Passalacqua

1
điều này hoạt động trong iOS <7, nhưng dẫn đến hành vi kỳ lạ trong iOS 7.
Ben H

1
Hoạt động tuyệt vời cho iOS 8!
Evan R

4
Vivek: Hãy cho tôi biết bạn đã cố gắng gì và hãy lịch sự suy nghĩ trước khi bỏ phiếu tiêu cực.
Nitin

7
phương pháp này thực sự loại bỏ một bộ điều khiển chế độ xem khỏi ngăn xếp nhưng dường như cũng có một ngăn xếp điều hướng không bị ảnh hưởng. Hành vi tôi nhận được trong ios 8.4 là như thế này: giả sử chúng tôi có bộ điều khiển 1 2 3 4 5. Tôi loại bỏ 4, nút quay lại hiển thị trên 5 không bị ảnh hưởng. Tôi bấm lại, nó cho thấy 3 nhưng danh hiệu 4. Tôi bấm lại một lần nữa, nó cho thấy 3 với danh hiệu 3
Radu Simionescu

49

Trước tiên, bạn có thể lấy tất cả các bộ điều khiển chế độ xem trong mảng và sau đó sau khi kiểm tra với lớp bộ điều khiển chế độ xem tương ứng, bạn có thể xóa lớp bạn muốn.

Đây là đoạn mã nhỏ:

NSArray* tempVCA = [self.navigationController viewControllers];

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

Tôi nghĩ rằng điều này sẽ làm cho công việc của bạn dễ dàng hơn.


Cái này có thể được sử dụng cho nhiều mục đích. Cảm ơn :)
Hemang

10
Khi tôi sử dụng điều khiển này, bộ điều khiển được gỡ bỏ đúng cách. Nhưng khi tôi sử dụng nút "Quay lại", thanh điều hướng của tôi hiển thị thông tin của viewController đã bị loại bỏ. Có ai khác nhận được hành vi kỳ lạ này không và làm thế nào tôi có thể sửa chữa nó?
Robin Ellerkmann

1
@Robin Ellerkmann bạn đã tìm ra giải pháp cho vấn đề đó chưa? tôi đang xóa bộ điều khiển chế độ xem nhưng nút quay lại vẫn ở thanh điều hướng.
Mehmet Emre

2
@MehmetEm có phải tôi đang sử dụng Swift 2.1 với self.navigationController? .ViewControllers.removeLast (). Điều này làm việc khá tốt cho tôi.
Robin Ellerkmann

1
Khi tôi ở trong 4 bộ nhớ viewcontroller là 80MB khi đăng xuất tất cả viewcontroller sẽ bị xóa. Bộ nhớ còn 80MB. Vì vậy, bộ nhớ không được giải phóng. :(
Anil Gupta

39

Swift 3 & 4/5

self.navigationController!.viewControllers.removeAll()

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

Swift 2.1

xóa tất cả:

self.navigationController!.viewControllers.removeAll()

loại bỏ tại chỉ mục

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

Có nhiều hành động khả thi hơn như removeFirst, range, v.v.


3
Nhìn vào câu trả lời của bạn, tôi đã có một ý tưởng cho quy trình làm việc của dự án của mình. Cảm ơn rất nhiều.
Anirudha Mahale

Remove này NavigationController nó tự, không làm sạch một chồng xem bộ điều khiển
Daniel Beltrami

16

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

3
return !vc.isKind(of: MyViewController.self) && !vc.isKind(of: MyViewController2.self)sẽ thực hiện công việc trong một dòng :-)
Đánh dấu

10

Sử dụng setViewControllerschức năng từ UINavigationControllerlà cách tốt nhất. Ngoài ra còn có animatedtham số để kích hoạt hoạt ảnh.

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

Ví dụ trong nhanh chóng cho câu hỏi

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

Tôi đã thử những cách khác như [tempVC removeFromParentViewController];. Nó tạo ra hành vi kỳ lạ, điều hướng ViewController bị loại bỏ vẫn hiển thị khi bật lại như được @ robin-ellerkmann báo cáo


5
Đây thực sự là giải pháp tốt nhất: xóa VC khỏi mảng navigationController? .ViewControllers và sử dụng setViewControllers để gán mảng mới. Tôi cũng đã kiểm tra zombie hoặc chu trình tham chiếu, nó an toàn.
OhadM

Tôi xác nhận rằng đó là một giải pháp tuyệt vời: Tôi thực sự đang sử dụng setViewControllers(_:animated:)kỹ thuật đó theo cả hai cách: để bật nhiều bộ điều khiển và đẩy nhiều bộ điều khiển.
Cœur

8

Swift 2.0:

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

2
Vì vậy, bạn không buộc unwrapping bộ điều khiển chuyển hướng, bạn có thể làm cho nó trở thành một câu lệnh ifif var navArray = ... { ... }
Kiley

6

Swift 5, Xcode 11.3

Tôi thấy cách tiếp cận này đơn giản bằng cách chỉ định (các) bộ điều khiển chế độ xem bạn muốn loại bỏ khỏi ngăn xếp điều hướng.

extension UINavigationController {

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

Ví dụ sử dụng:

navigationController.removeViewController(YourViewController.self)

5

Nếu bạn đang cố gắng chuyển sang bộ điều khiển chế độ xem thứ 2 từ bộ điều khiển chế độ xem thứ 5 (bỏ qua bộ điều khiển chế độ xem thứ 3 và thứ 4), bạn muốn sử dụng [self.navigationController popToviewController:secondViewController].

Bạn có thể lấy secondViewControllertừ ngăn xếp bộ điều khiển điều hướng.

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

1
Không muốn bật bộ điều khiển chế độ xem hiện tại. Bộ điều khiển chế độ xem hiện tại sẽ vẫn còn nguyên vẹn. Nhưng tôi cần phải bật 2 viewcontrollers nằm dưới nó trong ngăn xếp
Jean Paul Scott

@JeanPaulScott. Tôi tự hỏi Tại sao bạn lại muốn làm điều đó, nếu không phải vì xuất hiện?!.
Vignesh

Có một trường hợp mà tôi sẽ có các trường hợp khác nhau của cùng một bộ điều khiển chế độ xem được đẩy vào ngăn xếp. Vì vậy, khi một phiên bản mới được tạo và đẩy vào ngăn xếp, tôi muốn bật ra phiên bản trước đó và bộ điều khiển chế độ xem được liên kết với nó.
Jean Paul Scott,

@Vignesh Điều này sẽ không hoạt động như yêu cầu trong iOS 7 vì cử chỉ 'vuốt để bật'
Dennis Pashkov

@JeanPaulScott để đạt được những gì bạn muốn, điều an toàn nhất là bật hai lần trước khi đẩy phiên bản trình điều khiển chế độ xem mới của bạn.
Radu Simionescu

4

Dùng cái này

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

Nó sẽ chăm sóc các ViewControllers của navigationController. viewControllers và cả một navigationItems được xếp chồng lên nhau trong navigationBar.

Lưu ý: Hãy chắc chắn gọi nó ít nhất sau viewDidAppear


1
Phương pháp này hoạt động hoàn hảo đối với tôi trong Swift 5, Xcode 10.3 ... nếu let navVCsCount = navigationController? .ViewControllers.count {self.navigationController? .ViewControllers.removeSubrange (navVCsCount-3 .. <navVCsCount - 1)}
Kedar Sukerkar

2

Giải pháp này đã làm việc cho tôi trong nhanh chóng 4:

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

chỉ số bộ điều khiển chế độ xem hiện tại của bạn trong ngăn xếp là:

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

Đảm bảo gọi hàm tiện ích này sau viewDidDisappear () của bộ điều khiển trước đó hoặc viewDidAppear () của bộ điều khiển mới


1

Chi tiết

  • Swift 5.1, Xcode 11.3.1

Giải pháp

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

Sử dụng

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 }

Đầy đủ mẫu

Đừng quên dán mã giải pháp vào đây

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

Kết quả

nhập mô tả hình ảnh ở đây


0

Tôi đã viết một phần mở rộng với phương thức loại bỏ tất cả các bộ điều khiển giữa gốc và trên cùng, trừ khi được chỉ định khác.

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

}

Nếu bạn muốn sử dụng phạm vi (ví dụ: 2 đến 5), bạn chỉ có thể sử dụng

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

Đã thử nghiệm trên iOS 12.2, Swift 5


0

// loại bỏ các bộ điều khiển chế độ xem theo tên lớp khỏi ngăn xếp và sau đó loại bỏ chế độ xem hiện tại.

 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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.