Mam stos nawigacji, na przykład z 5 UIViewControllers. Chcę usunąć trzeci i czwarty kontroler widoku ze stosu jednym kliknięciem przycisku w piątym kontrolerze widoku. Czy da się to zrobić? Jeśli tak to jak?
iphone
ios
uiviewcontroller
uinavigationcontroller
Jean Paul Scott
źródło
źródło
Możesz najpierw pobrać wszystkie kontrolery widoku w tablicy, a następnie po sprawdzeniu z odpowiednią klasą kontrolera widoku możesz usunąć ten, który chcesz.
Oto mały fragment kodu:
NSArray* tempVCA = [self.navigationController viewControllers]; for(UIViewController *tempVC in tempVCA) { if([tempVC isKindOfClass:[urViewControllerClass class]]) { [tempVC removeFromParentViewController]; } }
Myślę, że to ułatwi ci pracę.
źródło
Swift 3 i 4/5
self.navigationController!.viewControllers.removeAll()
self.navigationController?.viewControllers.remove(at: "insert here a number")
Swift 2.1
Usuń wszystko:
self.navigationController!.viewControllers.removeAll()
usunąć w indeksie
self.navigationController?.viewControllers.removeAtIndex("insert here a number")
Istnieje wiele możliwych akcji, takich jak removeFirst, range itp.
źródło
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 } })
źródło
return !vc.isKind(of: MyViewController.self) && !vc.isKind(of: MyViewController2.self)
wykonałby pracę w jednej linii :-)Korzystanie
setViewControllers
z funkcji fromUINavigationController
to najlepszy sposób. Jest teżanimated
parametr włączający animację.func setViewControllers(_ viewControllers: [UIViewController], animated: Bool)
Szybki przykład na pytanie
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) }
Próbowałem innych sposobów, takich jak
[tempVC removeFromParentViewController];
. Dziwnie zachowuje się, usunięto nawigację ViewController nadal wyświetlaną po wyskakującym okienku, jak zgłosił @ robin-ellerkmannźródło
setViewControllers(_:animated:)
techniki na dwa sposoby: do otwierania wielu kontrolerów i wciskania wielu kontrolerów.Swift 2.0:
var navArray:Array = (self.navigationController?.viewControllers)! navArray.removeAtIndex(navArray.count-2) self.navigationController?.viewControllers = navArray
źródło
if var navArray = ... { ... }
Swift 5, Xcode 11.3
Znalazłem to proste podejście, określając, które kontrolery widoku chcesz usunąć ze stosu nawigacji.
extension UINavigationController { func removeViewController(_ controller: UIViewController.Type) { if let viewController = viewControllers.first(where: { $0.isKind(of: controller.self) }) { viewController.removeFromParent() } } }
Przykładowe zastosowanie:
navigationController.removeViewController(YourViewController.self)
źródło
Jeśli próbujesz przejść do kontrolera widoku drugiego z kontrolera widoku piątego (pomijając trzeci i czwarty), chciałbyś użyć
[self.navigationController popToviewController:secondViewController]
.Możesz pobrać
secondViewController
ze stosu kontrolera nawigacji.secondViewController = [self.navigationController.viewControllers objectAtIndex:yourViewControllerIndex];
źródło
Użyj tego
if let navVCsCount = navigationController?.viewControllers.count { navigationController?.viewControllers.removeSubrange(Range(2..<navVCsCount - 1)) }
Zadba o ViewControllers w navigationController. viewControllers, a także navigationItems ułożone w navigationBar.
Uwaga: pamiętaj, aby wywołać to co najmniej po viewDidAppear
źródło
Detale
Rozwiązanie
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()!) } }
Stosowanie
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 }
Pełna próbka
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 } } }
Wynik
źródło
To rozwiązanie zadziałało dla mnie w Swift 4:
let VCCount = self.navigationController!.viewControllers.count self.navigationController?.viewControllers.removeSubrange(Range(VCCount-3..<VCCount - 1))
Twój aktualny indeks kontrolera widoku na stosie to:
self.navigationController!.viewControllers.count - 1
źródło
Swift 5.1, Xcode 11
extension UINavigationController{ public func removePreviousController(total: Int){ let totalViewControllers = self.viewControllers.count self.viewControllers.removeSubrange(totalViewControllers-total..<totalViewControllers - 1) }}
Pamiętaj, aby wywołać tę funkcję narzędzia po funkcji viewDidDisappear () poprzedniego kontrolera lub viewDidAppear () nowego kontrolera
źródło
Napisałem rozszerzenie z metodą, która usuwa wszystkie kontrolery między rootem a top, chyba że określono inaczej.
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) }
}
Jeśli chcesz użyć zakresu (na przykład: od 2 do 5), możesz po prostu użyć
let range = 2 ..< 5 viewControllers.removeSubrange(range)
Testowano na iOS 12.2, Swift 5
źródło
// usunięcie kontrolerów widoku według nazw klas ze stosu, a następnie odrzucenie bieżącego widoku.
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)
źródło