Como verificar se um controlador de exibição é apresentado modalmente ou pressionado em uma pilha de navegação?

126

Como posso, no meu código de controlador de exibição, diferenciar entre:

  • apresentado modalmente
  • empurrado na pilha de navegação

Ambos presentingViewControllere isMovingToParentViewControllersão YESnos dois casos, portanto, não são muito úteis.

O que complica as coisas é que meu controlador de exibição pai às vezes é modal, no qual o controlador de exibição a ser verificado é pressionado.

Acontece que meu problema é que eu incorporo o meu HtmlViewControllerem um UINavigationControllerque é então apresentado. É por isso que minhas próprias tentativas e as boas respostas abaixo não estavam funcionando.

HtmlViewController*     termsViewController = [[HtmlViewController alloc] initWithDictionary:dictionary];
UINavigationController* modalViewController;

modalViewController = [[UINavigationController alloc] initWithRootViewController:termsViewController];
modalViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentViewController:modalViewController
                   animated:YES
                 completion:nil];

Acho melhor dizer ao meu controlador de exibição quando é modal, em vez de tentar determinar.

assuntos de significado
fonte

Respostas:

125

Tome com um grão de sal, não testei.

- (BOOL)isModal {
     if([self presentingViewController])
         return YES;
     if([[[self navigationController] presentingViewController] presentedViewController] == [self navigationController])
         return YES;
     if([[[self tabBarController] presentingViewController] isKindOfClass:[UITabBarController class]])
         return YES;

    return NO;
 }
ColdLogic
fonte
12
Eu encontrei isso em outro post SO. Mas, não funciona se o pai do controlador de exibição empurrado for um modal; qual é a situação que estou tendo.
significado-importa
2
Como escrevi, presentingViewControllerestá sempre YESno meu caso; não ajuda.
significado-importa
3
presentingViewControllerretorna YESpara VC empurrado, quando há um UITabBarControllersendo definido como raiz. Então, não é adequado no meu caso.
Yevhen Dubinin
5
Isso não funciona se você apresentar um controlador de exibição, e ele empurra outro.
Lee
3
"Isso não funciona se você apresentar um controlador de exibição e ele empurra outro" Essa não é a intenção, o controlador de exibição pressionado não está sendo apresentado.
Colin Swelin
87

No Swift :

Adicione um sinalizador para testar se é um modal pelo tipo de classe:

// MARK: - UIViewController implementation

extension UIViewController {

    var isModal: Bool {

        let presentingIsModal = presentingViewController != nil
        let presentingIsNavigation = navigationController?.presentingViewController?.presentedViewController == navigationController
        let presentingIsTabBar = tabBarController?.presentingViewController is UITabBarController

        return presentingIsModal || presentingIsNavigation || presentingIsTabBar
    }
}
O rei da bruxaria
fonte
4
Deve ser melhor em um var, comovar isModal: Bool {}
malinois
1
@malinois mudou
YannSteph
O que o último falseparâmetro na returninstrução faz?
damjandd
Você precisa mudar para deixar presentingIsNavigation = navigationController .presentingViewController .presentedViewController == navigationController && navigationController = nil?!
famfamfam
78

Você negligenciado um método: isBeingPresented.

isBeingPresented é verdadeiro quando o controlador de exibição está sendo apresentado e falso quando está sendo pressionado.

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    if ([self isBeingPresented]) {
        // being presented
    } else if ([self isMovingToParentViewController]) {
        // being pushed
    } else {
        // simply showing again because another VC was dismissed
    }
}
rmaddy
fonte
2
Eu tentei isso também antes de postar, e ele não funciona, isBeingPresentedé NO. Mas agora vejo o motivo: estou incorporando meu controlador de exibição apresentado em um UINavigationControllere é esse que estou pressionando.
significado-importa
1
Você não pode empurrar um controlador de navegação. Talvez você tenha dito que está apresentando o controlador de navegação.
Rddy12
3
@jowie Use p, não poao imprimir um valor primitivo. poé para imprimir objetos.
rmaddy
37
Documentação para isBeingPresented- Este método retorna YES apenas quando chamado de dentro dos métodos viewWillAppear: e viewDidAppear:.
funct7
4
@Terrance Parece que a documentação mais recente não mostra essas informações, mas costumava estar lá. A isBeingPresented, isBeingDismissed, isMovingFromParentViewControllere isMovingToParentViewControllersão válidos apenas dentro das 4 view[Will|Did][Disa|A]ppearmétodos.
rmaddy
29

Swift 5
Aqui está a solução que resolve o problema mencionado nas respostas anteriores, quando isModal()retorna truese pressionado UIViewControllerestiver em uma UINavigationControllerpilha apresentada .

extension UIViewController {
    var isModal: Bool {
        if let index = navigationController?.viewControllers.firstIndex(of: self), index > 0 {
            return false
        } else if presentingViewController != nil {
            return true
        } else if navigationController?.presentingViewController?.presentedViewController == navigationController {
            return true
        } else if tabBarController?.presentingViewController is UITabBarController {
            return true
        } else {
            return false
        }
    }
}

Isso funciona para mim até agora. Se algumas otimizações, por favor, compartilhe.

Jonauz
fonte
Por que você precisa verificar tabBarController?.presentingViewController is UITabBarController ? Importa se esse presentingViewControllertambém é um UITabBarController?
Hlung 21/04
E se navigationController for nulo, isModalretornará true. Isso é pretendido?
Hlung 21/04
28

self.navigationController! = nil significa que está em uma pilha de navegação.

Para lidar com o caso de o atual controlador de exibição ser pressionado enquanto o controlador de navegação é apresentado de forma modal, adicionei algumas linhas de código para verificar se o atual controlador de exibição é o controlador raiz na pilha de navegação.

extension UIViewController {
    var isModal: Bool {
        if let index = navigationController?.viewControllers.firstIndex(of: self), index > 0 {
            return false
        } else if presentingViewController != nil {
            return true
        } else if let navigationController = navigationController, navigationController.presentingViewController?.presentedViewController == navigationController {
            return true
        } else if let tabBarController = tabBarController, tabBarController.presentingViewController is UITabBarController {
            return true
        } else {
            return false
        }
    }
}
Jibeex
fonte
Bem, em geral, quando você apresenta modalmente, coloca o viewController em um navigationController e o apresenta. Se for esse o caso, sua declaração estaria errada, no entanto, no código, esse caso é tratado. Por favor, melhorar a sua resposta :)
E-Riddie
bom trabalho que lida com todos os casos de uso. espaço para um pouco de refatoração provavelmente, mas ainda voto a favor !!
Jean Raymond Daher
12

Swift 4

var isModal: Bool {
    return presentingViewController != nil ||
           navigationController?.presentingViewController?.presentedViewController === navigationController ||
           tabBarController?.presentingViewController is UITabBarController
}
Charlton Provatas
fonte
Swift 4.2 / iOS 12. Ainda funciona bem, mas saiba que o navigationController? .PresentingViewController? .PresentedViewController === navigationController será avaliado como verdadeiro se ambos forem nulos (por exemplo, se você o chamar em um controlador de exibição que ainda não foi apresentado).
Eli Burke
7

Rápido 5. Limpo e simples.

if navigationController.presentingViewController != nil {
    // Navigation controller is being presented modally
}
Kirill Kudaev
fonte
1
isso fez o truque para mim
Radu Ursache
3

Como muitas pessoas sugerem aqui, que os métodos de "verificação" não funcionam bem para todos os casos, no meu projeto eu encontrei uma solução para gerenciar isso manualmente. O ponto é que geralmente gerenciamos a apresentação por conta própria - não é isso que acontece nos bastidores e precisamos fazer uma introspecção.

DEViewController.h Arquivo:

#import <UIKit/UIKit.h>

// it is a base class for all view controllers within a project
@interface DEViewController : UIViewController 

// specify a way viewcontroller, is presented  by another viewcontroller
// the presented view controller should manually assign the value to it
typedef NS_ENUM(NSUInteger, SSViewControllerPresentationMethod) {
    SSViewControllerPresentationMethodUnspecified = 0,
    SSViewControllerPresentationMethodPush,
    SSViewControllerPresentationMethodModal,
};
@property (nonatomic) SSViewControllerPresentationMethod viewControllerPresentationMethod;

// other properties/methods...
@end

As apresentações agora podem ser gerenciadas desta maneira:

pressionado na pilha de navegação:

// DETestViewController inherits from DEViewController
DETestViewController *vc = [DETestViewController new];
vc.viewControllerPresentationMethod = SSViewControllerPresentationMethodPush;
[self.navigationController pushViewController:vc animated:YES];

apresentado de forma modal com a navegação:

DETestViewController *vc = [DETestViewController new];
vc.viewControllerPresentationMethod = SSViewControllerPresentationMethodModal;
UINavigationController *nav = [[UINavigationController alloc]
                               initWithRootViewController:vc];
[self presentViewController:nav animated:YES completion:nil];

apresentado modalmente:

DETestViewController *vc = [DETestViewController new];
vc.viewControllerPresentationMethod = SSViewControllerPresentationMethodModal;
[self presentViewController:vc animated:YES completion:nil];

Além disso, DEViewControllerpoderíamos adicionar um fallback para "verificar" se a propriedade mencionada acima é igual a SSViewControllerPresentationMethodUnspecified:

- (BOOL)isViewControllerPushed
{
    if (self.viewControllerPresentationMethod != SSViewControllerPresentationMethodUnspecified) {
        return (BOOL)(self.viewControllerPresentationMethod == SSViewControllerPresentationMethodPush);
    }

    else {
        // fallback to default determination method
        return (BOOL)self.navigationController.viewControllers.count > 1;
    }
}
Yevhen Dubinin
fonte
3

Supondo que todos os viewControllers que você apresenta modalmente sejam agrupados dentro de um novo navigationController (o que você sempre deve fazer de qualquer maneira), você pode adicionar essa propriedade ao seu VC.

private var wasPushed: Bool {
    guard let vc = navigationController?.viewControllers.first where vc == self else {
        return true
    }

    return false
}
Demóstese
fonte
1
o que você deve sempre fazer de qualquer maneira - explique por que?
Alexander Abakumov 04/04
Alexander, você não deveria, realmente.
nickdnk
2

Para detectar que o seu controlador foi pressionado ou não, use apenas o código abaixo em qualquer lugar que você desejar:

if ([[[self.parentViewController childViewControllers] firstObject] isKindOfClass:[self class]]) {

    // Not pushed
}
else {

    // Pushed
}

Espero que este código possa ajudar alguém ...

Arash Zeinoddini
fonte
1
Este método não funciona quando você usa a mesma classe de controlador de exibição em vários locais, uma vez que verifica apenas a classe dela. Você pode verificar explicitamente a igualdade.
gklka
1

self.navigationController != nil significaria que está em uma pilha de navegação.

Daniel
fonte
25
Ainda pode estar em um controlador de navegação modal
ColdLogic
Portanto, 'modal' e 'push on navigation stack' não são mutuamente exclusivos. Pensar que isso depende do contexto, mas verificar se self.navigationController não é nulo responde se é um controlador de exibição de um controlador de navegação.
Daniel
@ Daniel A diferença está entre "empurrado" e "apresentado". "Modal' não tem nada a ver com isso eu acredito. 'ColdLogic' significava 'apresentou' quando disseram 'modal'.
rmaddy
1
if let navigationController = self.navigationController, navigationController.isBeingPresented {
    // being presented
}else{
    // being pushed
}
mkto
fonte
0

Se você estiver usando o ios 5.0 ou posterior, use este código

-(BOOL)isPresented
{
    if ([self isBeingPresented]) {
        // being presented
         return YES;
    } else if ([self isMovingToParentViewController]) {
        // being pushed
         return NO;
    } else {
        // simply showing again because another VC was dismissed
         return NO;
    }
}
Shahbaz Abbasi
fonte
0

Swift 5
Esta extensão prática lida com mais alguns casos do que as respostas anteriores. Esses casos são VC (view controller) é o VC raiz da janela do aplicativo, o VC é adicionado como filho ao VC pai. Ele tenta retornar true somente se o viewcontroller for apresentado de forma modal.

extension UIViewController {
    /**
      returns true only if the viewcontroller is presented.
    */
    var isModal: Bool {
        if let index = navigationController?.viewControllers.firstIndex(of: self), index > 0 {
            return false
        } else if presentingViewController != nil {
            if let parent = parent, !(parent is UINavigationController || parent is UITabBarController) {
                return false
            }
            return true
        } else if let navController = navigationController, navController.presentingViewController?.presentedViewController == navController {
            return true
        } else if tabBarController?.presentingViewController is UITabBarController {
            return true
        }
        return false
    }
}

Graças à resposta de Jonauz . Novamente, há espaço para mais otimizações. Por favor, discuta sobre o caso que precisa ser tratado na seção de comentários.

Mehedi Hasan
fonte
-1

Para quem está se perguntando, como dizer ao ViewController que ele está sendo apresentado

se Aestá apresentando / pressionandoB

  1. Definir um enum e propertyemB

    enum ViewPresentationStyle {
        case Push
        case Present
    }
    
    //and write property 
    
    var vcPresentationStyle : ViewPresentationStyle = .Push //default value, considering that B is pushed 
  2. Agora em A controlador de exibição, diga Bse ele está sendo apresentado / enviado atribuindopresentationStyle

    func presentBViewController() {
        let bViewController = B()
        bViewController.vcPresentationStyle = .Present //telling B that it is being presented
        self.presentViewController(bViewController, animated: true, completion: nil)
    }
  3. Uso no BView Controller

    override func viewDidLoad() {
        super.viewDidLoad()
    
        if self.vcPresentationStyle == .Present {
            //is being presented 
        }
        else {
            //is being pushed
        }
    
    }
Saif
fonte
-2
id presentedController = self.navigationController.modalViewController;
if (presentedController) {
     // Some view is Presented
} else {
     // Some view is Pushed
}

Isso permitirá que você saiba se o viewController é apresentado ou enviado por push

iCoder86
fonte
4
Esta propriedade está obsoleta.
Morkrom