У меня есть стек навигации, скажем, 5 UIViewController. Я хочу удалить 3-й и 4-й контроллеры просмотра в стеке одним нажатием кнопки в 5-м контроллере просмотра. Можно ли это сделать? Если да, то как?
iphone
ios
uiviewcontroller
uinavigationcontroller
Жан Поль Скотт
источник
источник
Вы можете сначала получить все контроллеры представления в массиве, а затем, проверив соответствующий класс контроллера представления, вы можете удалить тот, который хотите.
Вот небольшой фрагмент кода:
NSArray* tempVCA = [self.navigationController viewControllers]; for(UIViewController *tempVC in tempVCA) { if([tempVC isKindOfClass:[urViewControllerClass class]]) { [tempVC removeFromParentViewController]; } }
Думаю, это облегчит вашу работу.
источник
Swift 3 и 4/5
self.navigationController!.viewControllers.removeAll()
self.navigationController?.viewControllers.remove(at: "insert here a number")
Swift 2.1
убрать все:
self.navigationController!.viewControllers.removeAll()
удалить по индексу
self.navigationController?.viewControllers.removeAtIndex("insert here a number")
Есть еще множество возможных действий, таких как removeFirst, range и т. Д.
источник
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 } })
источник
return !vc.isKind(of: MyViewController.self) && !vc.isKind(of: MyViewController2.self)
сделает работу в одну строчку :-)Лучше всего использовать
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источник
setViewControllers(_:animated:)
технику обоими способами: вставлять несколько контроллеров и нажимать несколько контроллеров.Swift 2.0:
var navArray:Array = (self.navigationController?.viewControllers)! navArray.removeAtIndex(navArray.count-2) self.navigationController?.viewControllers = navArray
источник
if var navArray = ... { ... }
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)
источник
Если вы пытаетесь перейти на контроллер 2-го представления с контроллера 5-го представления (пропуская 3-й и 4-й), вы хотели бы использовать
[self.navigationController popToviewController:secondViewController]
.Вы можете получить
secondViewController
из стека контроллера навигации.secondViewController = [self.navigationController.viewControllers objectAtIndex:yourViewControllerIndex];
источник
Использовать это
if let navVCsCount = navigationController?.viewControllers.count { navigationController?.viewControllers.removeSubrange(Range(2..<navVCsCount - 1)) }
Он позаботится о ViewControllers для navigationController. viewControllers, а также navigationItems, сложенные в navigationBar.
Примечание. Обязательно вызывайте его хотя бы после viewDidAppear.
источник
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 () нового контроллера.
источник
Детали
Решение
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 } } }
Результат
источник
Это решение сработало для меня в быстром 4:
let VCCount = self.navigationController!.viewControllers.count self.navigationController?.viewControllers.removeSubrange(Range(VCCount-3..<VCCount - 1))
ваш текущий индекс контроллера представления в стеке:
self.navigationController!.viewControllers.count - 1
источник
Я написал расширение с методом, который удаляет все контроллеры между корневым и верхним, если не указано иное.
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
источник
// удаление контроллеров представления по именам классов из стека, а затем удаление текущего представления.
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)
источник