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?

Adam Waite
fonte
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];
    }
}
Conheçer
fonte
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)
budidino
fonte
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.

chown
fonte
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

Zaraki
fonte
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")
        }
    }
}
Lubilis
fonte
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? :)

Me amarre
fonte
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)
Saifan Nadaf
fonte
Boa, o que eu estava procurando. Obrigado.
maddysan
6

Você também pode tentar este: -

[self.navigationController popToViewController:yourViewController animated:YES];
Leena
fonte
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];
Sabareesh
fonte
4
    NSMutableArray *newStack = [NSMutableArray arrayWithArray:[self.navigationController viewControllers]];
    [newStack removeLastObject];
    [newStack removeLastObject];
    [self.navigationController setViewControllers:newStack animated:YES];
patrick-fitzgerald
fonte
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.

Riajur Rahman
fonte
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.

Lucas
fonte
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];
   }
}
VaporwareWolf
fonte
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

Robotnik
fonte
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];
    }
}
Marca
fonte
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)
Glenn S
fonte
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)
            }
        }
}
Peeyush karnwal
fonte
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)
    }
}
Balázs Vincze
fonte