नेविगेशन स्टैक से व्यू-कंट्रोल्स को हटाना


92

मेरे पास एक नेविगेशन स्टैक है, जिसमें 5 UIViewControllers हैं। मैं 5 वें व्यू कंट्रौलर में एक बटन के क्लिक पर स्टैक में तीसरे और चौथे दृश्य नियंत्रक को हटाना चाहता हूं। क्या इसे करना संभव है? यदि हां, तो कैसे?

जवाबों:


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

आशा है कि यह आपकी मदद करेगा।

संपादित करें: स्विफ्ट कोड

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

मैंने इसे बांध दिया है और काम नहीं करता है। मुझे बताया गया था कि गुणों के साथ कुछ करने का कारण यह है कि यह देखने वालों को प्रभावित नहीं करता है।
नूह पसलाक्क्वा

1
इसने iOS <7 में काम किया, लेकिन iOS 7.
बेन एच

1
IOS 8 के लिए बढ़िया काम करता है!
Evan R

4
विवेक: मुझे दिखाओ कि तुमने क्या कोशिश की है और नकारात्मक वोट से पहले सोचने के लिए शिष्टाचार है।
नितिन

7
यह विधि वास्तव में स्टैक से एक व्यूकंट्रोलर को हटा देती है, लेकिन इसमें नेविगेशन नेविगेशन स्टैक भी लगता है जो प्रभावित नहीं होता है। IOS 8.4 में मुझे जो व्यवहार मिलता है वह इस तरह है: कहते हैं कि हमारे पास नियंत्रक 2 2 3 4 5. मैं 4 हटाता हूं, 5 पर दिखाया गया बैक बटन प्रभावित नहीं होता है। मैं वापस क्लिक करता हूं, यह 3 दिखाता है, लेकिन शीर्षक 4। मैं फिर से वापस क्लिक करता हूं, यह 3 को 3 के शीर्षक के साथ दिखाता है
राडू सिमियोनेस्कु

49

आप पहले सभी व्यू कंट्रोलर्स को ऐरे में प्राप्त कर सकते हैं और फिर संबंधित व्यू कंट्रोलर क्लास के साथ जांच करने के बाद, आप जो चाहें उसे हटा सकते हैं।

यहाँ कोड का छोटा टुकड़ा है:

NSArray* tempVCA = [self.navigationController viewControllers];

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

मुझे लगता है कि इससे आपका काम आसान हो जाएगा।


यह एक बहु प्रयोजन के लिए इस्तेमाल किया जा सकता है। धन्यवाद :)
हेमंग

10
जब मैं इसका उपयोग करता हूं तो नियंत्रक ठीक से हटा दिया जाता है। लेकिन जब मैं "बैक" बटन का उपयोग करता हूं तो मेरा नेविगेशन बार हटाए गए व्यूकंट्रोलर की जानकारी दिखाता है। क्या किसी और को यह अजीब व्यवहार प्राप्त होता है और मैं इसे कैसे ठीक कर सकता हूं?
बजे रॉबिन एल्करमैन

1
@ रोबिन एल्केरमैन क्या आपने उस समस्या का हल खोजा? मैं viewcontroller को हटा रहा हूं, लेकिन नेविगेशन बार में बैक बटन रहता है।
मेहमत एमरे

2
@MehmetEmre मैं Swift 2.1 का उपयोग self.navigationController? .ViewControllers.removeLast () के साथ करता हूं। यह मेरे लिए बहुत अच्छा काम करता है।
रॉबिन एल्करमैन

1
जब मैं 4 व्यूकंट्रोलर मेमोरी में था 80MB जब लॉग आउट किया गया तो सभी व्यू कॉन्ट्रोलर हटा दिए गए। मेमोरी अभी भी 80MB। इसलिए मेमोरी रिलीज नहीं हो रही है। :(
अनिल गुप्ता

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

अधिक संभावित क्रियाओं जैसे कि निष्कासन, सीमा आदि का एक गुच्छा।


3
आपके उत्तर को देखते हुए, मुझे अपने प्रोजेक्ट के वर्कफ़्लो के लिए एक विचार मिला। बहुत बहुत धन्यवाद।
अनिरुद्ध महाले

यह नेवीगेशन कंट्रोलर को स्वयं हटा दें, दृश्य नियंत्रकों के ढेर को साफ न करें
डैनियल बेल्ट्रामी

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

setViewControllersसे फ़ंक्शन का उपयोग करना 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
यह वास्तव में सबसे अच्छा समाधान है: नेविगेशनकोंट्रोलर से वीसी को हटा देना; .व्यूकंट्रोलर एरे और नए व्यू को असाइन करने के लिए सेटव्यू कंट्रोलर का उपयोग करना। मैंने लाश या संदर्भ चक्रों के लिए भी जाँच की है, यह सुरक्षित है।
OhadM

मैं पुष्टि करता हूं कि यह एक उत्कृष्ट समाधान है: मैं वास्तव setViewControllers(_:animated:)में दोनों तरीकों से उस तकनीक का उपयोग कर रहा हूं : कई नियंत्रक पॉप करने के लिए और कई नियंत्रक पुश करने के लिए।
कूर

8

स्विफ्ट 2.0:

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

2
तो आप नेविगेशन कंट्रोलर को उजागर करने के लिए मजबूर नहीं कर रहे हैं, आप इसे एक बयान कर सकते हैंif var navArray = ... { ... }
किली

6

स्विफ्ट 5, एक्सकोड 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

यदि आप 5th व्यू कंट्रोलर (3rd और 4th को छोड़ कर) से 2nd व्यू कंट्रोलर में जाने का प्रयास कर रहे हैं, तो आप उपयोग करना चाहेंगे [self.navigationController popToviewController:secondViewController]

आप secondViewControllerनेविगेशन कंट्रोलर स्टैक से प्राप्त कर सकते हैं ।

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

1
न ही वर्तमान दृश्यमापक को पॉप करना चाहते हैं। वर्तमान दृश्य नियंत्रण बरकरार रहना चाहिए। लेकिन मुझे स्टैक में इसके नीचे पड़े 2 व्यू-कंट्रोल्स को पॉप करने की आवश्यकता है
जीन पॉल स्कॉट

@JeanPaulScott। मुझे आश्चर्य है कि आप ऐसा क्यों करना चाहेंगे, अगर पॉपिंग के लिए नहीं?
विग्नेश

वहाँ एक मामला है जहाँ मैं एक ही दृश्यमंथोलर के विभिन्न उदाहरणों को स्टैक में धकेला जा रहा है। इसलिए जब एक नया उदाहरण बनाया जाता है और स्टैक में धकेल दिया जाता है, तो मैं पिछले उदाहरण और उस से जुड़े व्यू-कॉन्ट्रोलर को बाहर करना चाहता हूं।
जीन पॉल स्कॉट

@ विग्नेश ign स्वाइप टू पॉप ’जेस्चर की वजह से iOS 7 में आवश्यक रूप से काम नहीं करेगा
डेनिस पशकोव

@JeanPaulScott जो आप चाहते हैं उसे प्राप्त करने के लिए, अपने नए व्यू कंट्रोलर इंस्टेंस को पुश करने से पहले सबसे सुरक्षित चीज दो बार पॉप करना है।
राडू सिमियोनेस्कु

4

इसे इस्तेमाल करो

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

यह नेविगेशनकंट्रोलर के ViewControllers का ख्याल रखेगा। viewControllers और भी एक नेविगेशन।

नोट: कम से कम viewDidAppear के बाद इसे कॉल करना सुनिश्चित करें


1
यह विधि स्विफ्ट 5, Xcode 10.3 ... अगर जाने navVCsCount = navigationController .viewControllers.count {? Self.navigationController .viewControllers.removeSubrange (- 1 navVCsCount -3 .. <navVCsCount)} में मेरे लिए पूरी तरह से काम किया है
केदार Sukerkar

2

इस समाधान ने मेरे लिए स्विफ्ट 4 में काम किया:

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

स्टैक में आपका वर्तमान दृश्य नियंत्रक सूचकांक है:

self.navigationController!.viewControllers.count - 1

2

स्विफ्ट 5.1, एक्सकोड 11

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

पिछले नियंत्रक या viewDidAppear () के नए नियंत्रक के viewDisDisappear () के बाद इस उपयोगिता फ़ंक्शन को कॉल करना सुनिश्चित करें


1

विवरण

  • स्विफ्ट 5.1, एक्सकोड 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

मैंने विधि के साथ एक विस्तार लिखा है जो रूट और टॉप के बीच सभी नियंत्रकों को हटा देता है, जब तक कि अन्यथा निर्दिष्ट न हो।

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 पर परीक्षण, स्विफ्ट 5


0

// स्टैक से क्लास के नामों द्वारा व्यूकंट्रोलर्स को हटाना और फिर वर्तमान दृश्य को खारिज करना।

 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)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.