Как мне открыть два представления сразу из контроллера навигации?


92

Я хочу вернуться к третьему представлению в стеке навигации и вернуться к первому представлению.

Я знаю, как открыть одно представление сразу:

[self.navigationController popViewControllerAnimated:YES];

Но как мне сделать сразу два?


2
Мета-комментарий: ответ @lubilis - лучший. Самые популярные ответы были хорошими в свое время, но уже не актуальны.
n13,

Ответы:


133

Вы также можете попробовать это для перехода между стеком контроллера навигации

NSMutableArray *allViewControllers = [NSMutableArray arrayWithArray:[self.navigationController viewControllers]];
for (UIViewController *aViewController in allViewControllers) {
    if ([aViewController isKindOfClass:[RequiredViewController class]]) {
        [self.navigationController popToViewController:aViewController animated:NO];
    }
}

6
imo, это, безусловно, лучший метод, но вы должны ссылаться на свой uinavigation контроллер с помощью self.navigationcontroller
henghonglee

2
Я согласен, это лучшее решение, если пользователь хочет вернуть стек обратно в определенный контроллер просмотра. Допустим, вы не знаете, какой это контроллер представления, вы все равно можете реализовать систему, в которой вы бы указали, сколько контроллеров представления вы хотите вывести из стека и получить целевой контроллер представления из массива allViewControllers с помощью objectAtIndex: (allViewControllers.count - 1 - сумма). -1, потому что массивы, конечно, начинаются с нуля.
Джован

1
Люблю это решение. Именно то, что я искал
Designer023

3
Работает также при итерации по исходному массиву navigator-> viewControllers (нет необходимости преобразовывать его в изменяемый массив)
Арик Сигал,

НОТА! Это будет перебирать стек от начала до конца, чтобы быть точным, вы должны перевернуть стек. Потому что, если у вас есть несколько VC одного и того же типа, вы удалите неправильный VC в неправильном порядке в стеке.
StackUnderflow

72

Вот два UINavigationControllerрасширения, которые могут решить вашу проблему. Я бы рекомендовал использовать первый, который появляется в UIViewControllerопределенном классе:

extension UINavigationController {

  func popToViewController(ofClass: AnyClass, animated: Bool = true) {
    if let vc = viewControllers.filter({$0.isKind(of: ofClass)}).last {
      popToViewController(vc, animated: animated)
    }
  }

  func popViewControllers(viewsToPop: Int, animated: Bool = true) {
    if viewControllers.count > viewsToPop {
      let vc = viewControllers[viewControllers.count - viewsToPop - 1]
      popToViewController(vc, animated: animated)
    }
  }

}

и используйте это так:

// pop to SomeViewController class
navigationController?.popToViewController(ofClass: SomeViewController.self)

// pop 2 view controllers
navigationController?.popViewControllers(viewsToPop: 2)

3
Для Swift (вариант 1) вы можете заменить два removeLastна just removeLast(2).
Dominic K

А как насчет вызова методов жизненного цикла контроллеров? DidAppear и т. Д.
Майк Глухов

1
(Swift 4) У вас отсутствуют скобки в этой строке let vc = viewControllers[viewControllers.count - viewsToPop + 1], для правильной работы вам необходимо заменить их на: let vc = viewControllers[viewControllers.count - (viewsToPop + 1)]илиlet vc = viewControllers[viewControllers.count - viewsToPop - 1]
MMiroslav

@MMiroslav, вы правы. Я обновил свой ответ. Спасибо / Хвала;)
budidino

1
Этот ответ был обновлен после того, как я разместил свой ответ ниже. По сути копия моего кода ^ _ ^
lubilis

44

Вы можете перейти к «корневому» (первому) контроллеру представления с помощью popToRootViewControllerAnimated:

[self.navigationController popToRootViewControllerAnimated:YES];

// If you want to know what view controllers were popd:
// NSArray *popdViewControllers = [self.navigationController popToRootViewControllerAnimated:YES];

UINavigationControllerСсылка :

Извлекает все контроллеры представления в стек, кроме контроллера корневого представления, и обновляет отображение.

Возвращаемое значение
Массив контроллеров представления, извлекаемых из стека.


1
ха, не могу поверить, что я так долго искал такой простой ответ, спасибо!
Адам Уэйт

1
Swift 3: self.navigationController? .PopToRootViewController (анимированный: true);
dianakarenms

Именно то, что я искал!
Canucklesandwich

29
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:1] animated:YES];   

objectAtIndex: 1 -> вы можете передать любой индекс, который хотите вывести


Спасибо за фокус, я как раз собирался сделать это не так.
Линус

18

Swift 2 - xCode 7.3

Это может быть очень полезным расширением для вывода UIViewControllers:

extension UINavigationController {

    func popToViewControllerOfType(classForCoder: AnyClass) {
        for controller in viewControllers {
            if controller.classForCoder == classForCoder {
                popToViewController(controller, animated: true)
                break
            }
        }
    }

    func popViewControllers(controllersToPop: Int, animated: Bool) {
        if viewControllers.count > controllersToPop {
            popToViewController(viewControllers[viewControllers.count - (controllersToPop + 1)], animated: animated)
        } else {
            print("Trying to pop \(controllersToPop) view controllers but navigation controller contains only \(viewControllers.count) controllers in stack")
        }
    }
}

2
Это требует большего количества голосов ... Я как раз собирался написать это расширение. Я просто воспользуюсь твоей благодарностью;)
n13

1
@ n13, почему это лучше, чем ответ Будидино?
Crashalot

1
Использование расширения делает код намного чище. Вы можете взять ответ budidino и сделать из него расширение, но это сэкономит вам усилия. Также в этом ответе проверяются случаи ошибок и изящно обрабатываются ошибки (например, при попытке
выдать

1
Мне нравится этот ответ. Заставил меня переосмыслить и обновить опубликованный ответ. Я заставил свой код перейти к последнему экземпляру искомого класса в массиве viewControllers, потому что это, вероятно, желаемое поведение.
budidino 02

15

Если вы просто хотите вывести 2 сразу, потому что ваш rootViewController (способ) «глубже», то 2, вы можете подумать о добавлении категории в UIviewController, например:

UINavigationController + popTwice.h

#import <UIKit/UIKit.h>
@interface UINavigationController (popTwice)

- (void) popTwoViewControllersAnimated:(BOOL)animated;

@end

UINavigationController + popTwice.m

#import "UINavigationController+popTwice.h"

@implementation UINavigationController (popTwice)

- (void) popTwoViewControllersAnimated:(BOOL)animated{
    [self popViewControllerAnimated:NO];
    [self popViewControllerAnimated:animated];
}

@end

Импортируйте категорию #import "UINavigationController+popTwice.h"где-нибудь в своей реализации и используйте эту строку кода для одновременного вывода двух контроллеров:

[self.navigationController popTwoViewControllersAnimated:YES];

разве это не здорово? :)


6
что если вам нужно открыть три вида, вы напишете "UINavigationController + popThrice.m" ??????
Встреча

8
вы можете просто передать параметр для количества всплывающих контроллеров просмотра и обернуть [self popViewControllerAnimated: NO]; внутри цикла for count-1.
noRema

Это неправильный способ, если вы хотите использовать 2,3, ... любой контроллер идентифицирует его по циклу, а затем использует [self.navigationController popToViewControllerAnimated: YES] ;. Это вышеупомянутое очень плохое кодирование и может отображать мерцающий пользовательский интерфейс и плохой пользовательский интерфейс.
Vicky Dhas

10

Swift 4:

func popViewControllerss(popViews: Int, animated: Bool = true) {
    if self.navigationController!.viewControllers.count > popViews
    {
        let vc = self.navigationController!.viewControllers[self.navigationController!.viewControllers.count - popViews - 1]
         self.navigationController?.popToViewController(vc, animated: animated)
    }
}

Затем используйте этот метод

self.popViewControllerss(popViews: 2)

Хороший, что я искал. Спасибо.
maddysan

6

Вы также можете попробовать это: -

[self.navigationController popToViewController:yourViewController animated:YES];

6
//viewIndex = 1;
//viewIndex = 2;
//viewIndex = 3;

// **[viewControllers objectAtIndex: *put here your viewindex number*]

NSArray *viewControllers = [self.navigationController viewControllers];
[self.navigationController popToViewController:[viewControllers objectAtIndex:viewIndex] animated:NO];

4
    NSMutableArray *newStack = [NSMutableArray arrayWithArray:[self.navigationController viewControllers]];
    [newStack removeLastObject];
    [newStack removeLastObject];
    [self.navigationController setViewControllers:newStack animated:YES];

4

В Swift 3 вы можете вытащить один, два или несколько контроллеров просмотра из контроллера навигации таким образом

let viewControllers = self.navigationController!.viewControllers as [UIViewController]
    for aViewController:UIViewController in viewControllers {
        if aViewController.isKind(of: FromWhereYouWantToGoController.self) {
            _ = self.navigationController?.popToViewController(aViewController, animated: true)
        }
    }

Здесь FromWhereYouWantToGoController - это контроллер назначения. Надеюсь, это поможет.


3

Вы можете передать начальный контроллер представления (тот, к которому вы хотите вернуться), а затем вызвать эту строку в последнем представлении:

[self.navigationController popToViewController:yourInitialViewController animated:YES];

Л.


3

Я не видел здесь этого ответа. Если вы хотите вставить только несколько (не до корня), вы можете просто выполнить итерацию через self.navigationController.viewControllers, проверяя типы классов, или, если у вас есть ссылка, используйте это:

for (UIViewController *aViewController in self.navigationController.viewControllers) {
   if ([aViewController isKindOfClass:[SMThumbnailViewController class]]) {
      [self.navigationController popToViewController:aViewController animated:YES];
   }
}

2

вы можете вернуться к контроллеру корневого представления

[self.navigationController popToRootViewControllerAnimated:YES];

или, если представление, которое вы хотите открыть, не является первым, вам нужно будет снова открыть его в представлении предыдущего представленияWillAppear


2

Вот небольшая функция, которую я использую для возврата к X ViewControllers:

-(void)backMultiple:(id)data {
    int back = 2; //Default to go back 2 
    int count = [self.navigationController.viewControllers count];

    if(data[@"count"]) back = [data[@"count"] intValue];

    //If we want to go back more than those that actually exist, just go to the root
    if(back+1 > count) {
        [self.navigationController popToRootViewControllerAnimated:YES];
    }
    //Otherwise go back X ViewControllers 
    else {
        [self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:count-(back+1)] animated:YES];
    }
}

2

Версия Swift от (Swift 1.2 / Xcode 6.3.1), появляющаяся дважды или более

 var viewControllers = self.navigationController?.viewControllers
 viewControllers?.removeLast()
 viewControllers?.removeLast()
 self.navigationController?.setViewControllers(viewControllers, animated: true)

или

 var viewControllers = self.navigationController?.viewControllers
 var viewsToPop = 2
 var end = (viewControllers?.count)!
 var start = end - viewsToPop
 viewControllers?.removeRange(Range<Int>(start: start, end: end))
 self.navigationController?.setViewControllers(viewControllers, animated: true)

1

Вы можете использовать стек UIViewControllers. 1. Получите массив всех viewControllers в стеке. 2. Просмотрите весь массив и найдите желаемый viewController
, сопоставив тип класса. 3. Откройте viewController.

func popToSpecificViewC
{
let viewControllers: [UIViewController] = self.navigationController!.viewControllers as [UIViewController];
        for viewController:UIViewController in viewControllers
        {
            if viewController.isKind(of: WelcomeViewC.self)
            {
                _ = self.navigationController?.popToViewController(viewController, animated: true)
            }
        }
}

0

Используя простое расширение UINavigationController :

extension UINavigationController {
    func popViewControllers(_ count: Int) {
        guard viewControllers.count > count else { return }
        popToViewController(viewControllers[viewControllers.count - count - 1], animated: true)
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.