Como eu pop duas visualizações de uma vez em um controlador de navegação?


90

Eu quero ir para a terceira visualização na pilha de navegação de volta para a primeira visualização.

Eu sei como abrir uma visualização de uma vez:

[self.navigationController popViewControllerAnimated:YES];

Mas como faço dois de uma vez?


2
Meta comentário: a resposta @lubilis lá embaixo é a melhor. As respostas mais bem avaliadas eram boas na época, mas não são mais relevantes.
n13

Respostas:


132

Você pode tentar isso para pular entre a pilha do controlador de navegação também

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, este é o melhor método de longe, mas você deve consultar seu controlador de uinavigação usando self.navigationcontroller
henghonglee

2
Eu concordo, esta é a melhor solução se o usuário quiser colocar a pilha de volta em um determinado viewcontroller. Digamos que você não saiba qual viewcontroller é, você ainda pode implementar um sistema onde você especificaria quantos viewcontrollers você gostaria de retirar da pilha e obter o viewcontroller de destino da matriz allViewControllers com objectAtIndex: (allViewControllers.count - 1 - montante). -1 porque as matrizes são baseadas em zero, é claro.
Jovan,

1
Adoro esta solução. Exatamente o que eu estava procurando
Designer023

3
Funciona também ao iterar sobre a matriz navigator-> viewControllers original (não há necessidade de convertê-la em uma matriz mutável)
Arik Segal

NOTA! Isso irá iterar a pilha do início -> ao fim, para ser exatamente correto, você deve inverter a pilha. Porque se você tiver vários VC do mesmo tipo, removerá o VC errado na ordem errada da pilha.
StackUnderflow

71

Aqui estão duas UINavigationControllerextensões que podem resolver seu problema. Eu recomendaria usar o primeiro que aparecer em uma UIViewControllerclasse específica:

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

}

e use-o assim:

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

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

3
Para Swift (opção 1), você pode substituir os dois removeLastpor apenas removeLast(2).
Dominic K

E quanto aos métodos de chamada do ciclo de vida dos controladores? DidAppear and ect
Mike Glukhov

1
(Swift 4) Faltam colchetes nesta linha let vc = viewControllers[viewControllers.count - viewsToPop + 1], para funcionar corretamente você precisa substituí-lo por: let vc = viewControllers[viewControllers.count - (viewsToPop + 1)]oulet vc = viewControllers[viewControllers.count - viewsToPop - 1]
MMiroslav

@MMiroslav você está certo. Eu atualizei minha resposta. Obrigado / Hvala;)
budidino

1
Esta resposta foi atualizada depois que postei minha resposta abaixo. Basicamente, uma cópia do meu código ^ _ ^
lubilis

44

Você pode acessar o controlador de visualização "raiz" (primeiro) com popToRootViewControllerAnimated:

[self.navigationController popToRootViewControllerAnimated:YES];

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

UINavigationControllerReferência :

Exibe todos os controladores de exibição na pilha, exceto o controlador de exibição raiz e atualiza a exibição.

Valor de retorno
Uma matriz de controladores de exibição que são retirados da pilha.


1
ha, não acredito que passei tanto tempo procurando uma resposta tão simples, obrigado!
Adam Waite

1
Swift 3: self.navigationController? .PopToRootViewController (animado: verdadeiro);
dianakarenms

Precisamente o que eu estava procurando!
Canucklesandwich

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

objectAtIndex: 1 -> você pode passar qualquer índice para o qual deseja colocar


Obrigado pelo truque, eu estava prestes a fazer da maneira errada.
Linus

18

Swift 2 - xCode 7.3

Esta poderia ser uma extensão muito útil para pop 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
Isso precisa de mais votos ... Eu estava prestes a escrever essa extensão. Vou usar os seus agradecimentos;)
n13

1
@ n13 por que isso é melhor do que a resposta de budidino?
Crashalot

1
Usar uma extensão torna o código muito mais limpo. Você poderia pegar a resposta de Budidino e fazer uma extensão dela, mas esta lhe poupa o esforço. Além disso, esta resposta verifica os casos de erro e lida com os erros normalmente (por exemplo, tentando
abrir

1
Eu gosto dessa resposta. Me fez repensar e atualizar a resposta que postei. Eu fiz meu código saltar para a última instância da classe pesquisada na matriz viewControllers porque esse é provavelmente o comportamento desejado.
budidino

15

Se você quiser apenas pop 2 de uma vez porque seu rootViewController é (muito) 'mais profundo' que 2, você pode considerar adicionar uma categoria a UIviewController, por exemplo:

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

Importe a categoria #import "UINavigationController+popTwice.h"em algum lugar em sua implementação e use esta linha de código para abrir 2 controladores de uma vez:

[self.navigationController popTwoViewControllersAnimated:YES];

não é ótimo? :)


6
e se você precisar abrir três visualizações, você escreverá "UINavigationController + popThrice.m" ??????
Reunião em

8
você poderia apenas passar um parâmetro para o número de viewControllers a serem exibidos e envolver [self popViewControllerAnimated: NO]; dentro de um loop for, de contagem-1.
noRema

Esta não é a maneira correta, se você quiser potenciar para 2,3, ... qualquer controlador identifica-o por loop e então usa [self.navigationController popToViewControllerAnimated: YES] ;. O código mencionado acima é uma codificação muito ruim e pode mostrar a IU instável e uma experiência do usuário ruim.
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)
    }
}

Então use este método

self.popViewControllerss(popViews: 2)

Boa, o que eu estava procurando. Obrigado.
maddysan

6

Você também pode tentar este: -

[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

No Swift 3 , você pode ativar um, dois ou mais controladores de visualização do controlador de navegação como esse

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

Aqui, FromWhereYouWantToGoController é o controlador de destino. Espero que ajude.


3

Você pode passar o controlador de visualização inicial (aquele para o qual você deseja voltar) e, em seguida, chamar esta linha na última visualização:

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

EU.


3

Eu não vi essa resposta aqui. Se você quiser apenas alguns (não até a raiz), pode apenas iterar por meio de self.navigationController.viewControllers verificando os tipos de classe ou, se tiver uma referência, use isso:

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

2

você pode voltar para o controlador de visualização raiz

[self.navigationController popToRootViewControllerAnimated:YES];

ou, se a visualização que você deseja exibir não for a primeira, você precisará voltar ao viewWillAppear de sua visualização anterior


2

Aqui está uma pequena função que estou usando para voltar 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

Versão Swift a partir de (Swift 1.2 / Xcode 6.3.1) de popping duas ou mais

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

ou

 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

Você pode usar a pilha de UIViewControllers. 1. Busque a matriz de todos os viewControllers na pilha. 2. Itere por todo o array e encontre o viewController desejado
, combinando o tipo de classe. 3. Abra o 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

Usando uma extensão UINavigationController simples :

extension UINavigationController {
    func popViewControllers(_ count: Int) {
        guard viewControllers.count > count else { return }
        popToViewController(viewControllers[viewControllers.count - count - 1], animated: true)
    }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.