Görünüm denetleyicileri gezinti yığınından kaldırma


96

5 UIViewControllers diyelim ki bir gezinti yığınım var. 5. görüntü denetleyicisindeki bir düğmeye tıklayarak yığındaki 3. ve 4. görüntü denetleyicilerini kaldırmak istiyorum. Bunu yapmak mümkün mü? Öyleyse nasıl?

Yanıtlar:


171

Bu kodu kullanın ve keyfini çıkarın:

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

Umarım bu sana yardımcı olur.

Düzenleme: Swift Kodu

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

bunu bağladım ve çalışmıyor. Özelliklerle yapacağımız bir şeyin, görünüm denetleyicilerini serbest bırakmamasına neden olduğu söylendi.
Noah Passalacqua

1
bu iOS <7'de çalıştı, ancak iOS 7'de garip davranışlara neden oldu.
Ben H

1
İOS 8 için harika çalışıyor!
Evan R

4
Vivek: Bana neyi denediğinizi gösterin ve olumsuz oylamadan önce düşünmek için bir nezaket gösterin.
Nitin

7
bu yöntem aslında yığından bir görünüm denetleyicisini kaldırır, ancak etkilenmeyen bir gezinme öğeleri yığını da var gibi görünür. İos 8.4'te aldığım davranış şöyle: denetleyicilerimiz var diyelim 1 2 3 4 5. 4'ü kaldırıyorum, 5'te gösterilen geri düğmesi etkilenmiyor. Geri tıklıyorum, 3 gösteriyor ama 4 başlığı görünüyor. Tekrar tıklıyorum, 3 başlığıyla 3 gösteriyor
Radu Simionescu

49

Önce dizideki tüm görünüm denetleyicilerini alabilir ve ardından karşılık gelen görünüm denetleyicisi sınıfını kontrol ettikten sonra istediğinizi silebilirsiniz.

İşte küçük bir kod parçası:

NSArray* tempVCA = [self.navigationController viewControllers];

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

Bunun işinizi kolaylaştıracağını düşünüyorum.


Bu çok amaçlı kullanılabilir. Teşekkürler :)
Hemang

10
Bunu kullandığımda denetleyici düzgün bir şekilde kaldırıldı. Ancak "Geri" düğmesini kullandığımda gezinti çubuğum kaldırılan viewController'ın bilgilerini gösteriyor. Bu tuhaf davranışı başka kimse alıyor mu ve bunu nasıl düzeltebilirim?
Robin Ellerkmann

1
@Robin Ellerkmann bu soruna çözüm buldunuz mu? görüntü denetleyicisini kaldırıyorum ancak geri düğmesi gezinme çubuğunda kalıyor.
Mehmet Emre

2
@MehmetEmre Swift 2.1'i self.navigationController? .ViewControllers.removeLast () ile kullanıyorum. Bu benim için oldukça iyi çalışıyor.
Robin Ellerkmann

1
4 viewcontroller'deyken, tüm viewcontroller oturumunu kapatırken 80MB hafızası kaldırıldı. Bellek hala 80MB. Yani bellek serbest kalmıyor. :(
Anil Gupta

40

Hızlı 3 ve 4/5

self.navigationController!.viewControllers.removeAll()

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

Swift 2.1

hepsini kaldır:

self.navigationController!.viewControllers.removeAll()

dizinden kaldır

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

RemoveFirst, aralık vb. Gibi bir dizi olası eylem var.


3
Cevabınıza baktığımda, projemin iş akışı için bir fikrim var. Çok teşekkürler.
Anirudha Mahale

Bu, NavigationController'ı kendi kendine kaldırır, bir yığın görüntü denetleyicisini temizlemez
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)işi tek satırda yapacaktı :-)
Mark

10

Kullanılması setViewControllersgelen işlevi UINavigationControlleren iyi yoldur. animatedAnimasyonu etkinleştirmek için bir parametre de vardır .

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

Hızlı soru için örnek

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

Gibi başka yollar denedim [tempVC removeFromParentViewController];. Garip bir davranışa neden oluyor, ViewController gezintisi kaldırıldı, @ robin-ellerkmann tarafından bildirildiği gibi geri döndüğünde gösterilmeye devam ediyor


5
Bu aslında en iyi çözümdür: VC'yi navigationController? .ViewControllers dizisinden kaldırmak ve yeni diziyi atamak için setViewControllers kullanmak. Ayrıca zombileri veya referans döngülerini de kontrol ettim, güvenli.
OhadM

Bunun mükemmel bir çözüm olduğunu onaylıyorum: Aslında bu setViewControllers(_:animated:)tekniği her iki şekilde de kullanıyorum : birden çok denetleyiciyi açmak ve birden çok denetleyiciyi çalıştırmak için.
Cœur

9

Swift 5, Xcode 11.3

Gezinme yığınından hangi görünüm denetleyicilerini kaldırmak istediğinizi belirterek bu yaklaşımı basit buldum.

extension UINavigationController {

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

Örnek kullanım:

navigationController.removeViewController(YourViewController.self)

8

Swift 2.0:

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

2
Yani gezinme denetleyicisini açmaya zorlamazsınız, bir if ifadesi yapabilirsinizif var navArray = ... { ... }
Kiley

5

5. görünüm denetleyiciden 2. görünüm denetleyiciye geçmeye çalışıyorsanız (3. ve 4. atlayarak), kullanmak istersiniz [self.navigationController popToviewController:secondViewController].

secondViewControllerGezinti denetleyicisi yığınından elde edebilirsiniz .

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

1
Mevcut görüntü denetleyicisini açmak istemiyorum. Mevcut görüntü denetleyicisi sağlam kalmalıdır. Ancak yığının altında yatan 2 görüntü denetleyicisini patlatmam gerekiyor
Jean Paul Scott

@Evleneceksen Açmak için olmasa da neden bunu yapmak isteyesin acaba?
Vignesh

Aynı görüntü denetleyicisinin farklı örneklerinin yığına itildiği bir durum var. Bu yüzden, yeni bir örnek oluşturulduğunda ve yığına itildiğinde, önceki örneği ve bununla ilişkili görüntü denetleyicisini açmak istiyorum.
Jean Paul Scott

@Vignesh Bu, 'kaydırmak için kaydır' hareketi nedeniyle iOS 7'de gerektiği gibi çalışmaz
Dennis Pashkov

@JeanPaulScott istediğiniz şeyi elde etmek için en güvenli şey, yeni görüntü denetleyici örneğinizi zorlamadan önce iki kez pop yapmaktır.
Radu Simionescu

4

Bunu kullan

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

NavigationController'ın ViewControllers'ına bakacaktır. viewControllers ve ayrıca navigationBar'da yığılmış bir navigationItems.

Not: En azından viewDidAppear'dan sonra çağırdığınızdan emin olun.


1
Bu yöntem Swift 5, Xcode 10.3'te benim için mükemmel çalıştı ... eğer navVCsCount = navigationController? .ViewControllers.count {self.navigationController? .ViewControllers.removeSubrange (navVCsCount-3 .. <navVCsCount - 1)}
Kedar Sukerkar

3

Swift 5.1, Xcode 11

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

Önceki denetleyicinin viewDidDisappear () veya yeni denetleyicinin viewDidAppear () 'ından sonra bu yardımcı program işlevini çağırdığınızdan emin olun.


3

Detaylar

  • Swift 5.1, Xcode 11.3.1

Çözüm

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

Kullanım

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 }

Tam Örnek

Çözüm kodunu buraya yapıştırmayı unutmayın

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

Sonuç

görüntü açıklamasını buraya girin


2

Bu çözüm benim için hızlı 4'te çalıştı:

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

yığındaki mevcut görünüm denetleyici dizininiz:

self.navigationController!.viewControllers.count - 1

0

Aksi belirtilmedikçe, kök ve üst arasındaki tüm denetleyicileri kaldıran yöntemli bir uzantı yazdım.

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

}

Aralık kullanmak istiyorsanız (örneğin: 2 ila 5) sadece kullanabilirsiniz

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

İOS 12.2, Swift 5 üzerinde test edildi


0

// görünüm denetleyicilerini yığından sınıf adlarına göre kaldırmak ve ardından geçerli görünümü kapatmak.

 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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.