É possível determinar se ViewController é apresentado como modal?


Respostas:


96

Uma vez que se modalViewControllertornou obsoleto no iOS 6, aqui está uma versão que funciona para iOS 5+ e que compila sem avisos.

Objective-C:

- (BOOL)isModal {
    return self.presentingViewController.presentedViewController == self
      || (self.navigationController != nil && self.navigationController.presentingViewController.presentedViewController == self.navigationController)
      || [self.tabBarController.presentingViewController isKindOfClass:[UITabBarController class]];
}

Rápido:

var isModal: Bool {
    return self.presentingViewController?.presentedViewController == self
        || (self.navigationController != nil && self.navigationController?.presentingViewController?.presentedViewController == self.navigationController)
        || self.tabBarController?.presentingViewController is UITabBarController
}

Gorjeta de chapéu à resposta de Felipe.


2
bom apanhado, só tive que usá-lo novamente depois de muito tempo e percebi que a descontinuação aconteceu ... Editei minha resposta para que as pessoas comecem a procurar aqui o código correto ao usar iOS 6+, obrigado
Felipe Sabino

10
Não funciona se o controlador de visão pai for um modal no qual nosso controlador de visão é empurrado.
assuntos de significado

2
Há um bug, devemos verificar se ambos os lados são nulos, porque nil == nilretorna YES, e não é o resultado que queremos.
CocoaBob

1
@GabrielePetronella Você se importa se eu atualizar a resposta para incluir também uma implementação Swift do método?
Cachoeira Michael

1
@MichaelWaterfall que seria muito apreciado, obrigado
Gabriele Petronella

77

Se você está procurando pelo iOS 6+, esta resposta está obsoleta e você deve verificar a resposta de Gabriele Petronella


Não há uma maneira legal de fazer isso, como uma propriedade ou método nativo do UIKit. O que você pode fazer é verificar vários aspectos do seu controlador para garantir que ele seja apresentado como modal.

Então, para verificar se o controlador atual (representado selfno código abaixo) é apresentado de forma modal ou não, tenho a função abaixo ou em uma UIViewControllercategoria, ou (caso seu projeto não necessite de outros controladores UIKit, como UITableViewControllerpor exemplo) em um controlador de base do qual meus outros controladores herdam

-(BOOL)isModal {

     BOOL isModal = ((self.parentViewController && self.parentViewController.modalViewController == self) || 
            //or if I have a navigation controller, check if its parent modal view controller is self navigation controller
            ( self.navigationController && self.navigationController.parentViewController && self.navigationController.parentViewController.modalViewController == self.navigationController) || 
            //or if the parent of my UITabBarController is also a UITabBarController class, then there is no way to do that, except by using a modal presentation
            [[[self tabBarController] parentViewController] isKindOfClass:[UITabBarController class]]);

    //iOS 5+
    if (!isModal && [self respondsToSelector:@selector(presentingViewController)]) {

        isModal = ((self.presentingViewController && self.presentingViewController.modalViewController == self) || 
             //or if I have a navigation controller, check if its parent modal view controller is self navigation controller
             (self.navigationController && self.navigationController.presentingViewController && self.navigationController.presentingViewController.modalViewController == self.navigationController) || 
             //or if the parent of my UITabBarController is also a UITabBarController class, then there is no way to do that, except by using a modal presentation
             [[[self tabBarController] presentingViewController] isKindOfClass:[UITabBarController class]]);

    }

    return isModal;        

}

EDIT: Eu adicionei a última verificação para ver se um UITabBarController está sendo usado, e você apresenta outro UITabBarController como modal.

EDIT 2: adicionado cheque iOS 5+, onde UIViewControllernão responde parentViewControllermais, mas presentingViewControllersim.

EDITAR 3: criei uma essência para isso, apenas no caso de https://gist.github.com/3174081


Lembre-se de que a modalViewControllerpropriedade está obsoleta a partir do iOS 6. A documentação sugere o uso presentedViewController.
Bart Jacobs

@BartJacobs, bom ponto! Não olhei para esta resposta após o lançamento do iOS6, portanto, pode não estar atualizado. Vou tentar fazer alguns testes ainda na semana para atualizá-lo, tks!
Felipe Sabino

NSLog(@"%@", self.navigationController.parentViewController)impressões (null)- você poderia explicar por quê? Meu ViewController está conectado ao controlador de visualização modal por meio do navController no storyboard.
Romano

@oyatek você pode usar o pastebin ou algo semelhante e mostrar algum código?
Felipe Sabino

@Feilpe Eu encontrei o problema - .parentViewControllerestá obsoleto, .presentingViewControllerdeve ser usado em seu lugar.
Romano

35

No iOS5 +, como você pode ver em Referência de classe UIViewController , você pode obtê-lo na propriedade "presentationViewController".

PresentViewController O controlador de visualização que apresentou este controlador de visualização. (somente leitura)

@property (nonatomic, readonly) UIViewController * apresentandoViewController
Discussão

Se o controlador de exibição que recebeu esta mensagem for apresentado por outro controlador de exibição, esta propriedade mantém o controlador de exibição que o está apresentando. Se o controlador de visualização não for apresentado, mas um de seus ancestrais estiver sendo apresentado, esta propriedade mantém o controlador de visualização apresentando o ancestral mais próximo. Se nem o controlador de visualização nem qualquer um de seus ancestrais estiverem sendo apresentados, essa propriedade será nula.

Disponibilidade
Disponível no iOS 5.0 e posterior.
Declarado em
UIViewController.h


3
Funciona perfeitamente, use if (self.presentingViewController) {// Este é um viewContoller modal} else {// Este é um ViewController normal}
mashdup

2
IMHO, esta é a única resposta correta aqui. Basta verificar a presença de um presentingViewController. Ele também funcionará em controladores de visualização de contêiner, visto que atravessa automaticamente os ancestrais.
Daniel Rinser

17

Se não houver, você pode definir uma propriedade para this ( presentedAsModal) em sua subclasse UIViewController e configurá-la YESantes de apresentar o ViewController como uma visualização modal.

childVC.presentedAsModal = YES;
[parentVC presentModalViewController:childVC animated:YES];

Você pode verificar esse valor em sua viewWillAppearsubstituição.

Acredito que não haja uma propriedade oficial que defina como a vista é apresentada, mas nada impede que você crie a sua própria.


Certo e isso é o que eu fiz, mas eu estava procurando alguma outra solução legal. Obrigado.
morno

esta solução não funciona se você estiver apresentando um UINavigationControllercomo modal ... a menos que você crie um controlador de navegação personalizado apenas para adicionar esta propriedade. E depois disso, dentro dos controladores, você terá que continuar lançando self.navigationControllerpara esta classe personalizada toda vez que precisar verificar se o controlador está apresentado como modal
Felipe Sabino

8

A resposta de Petronella não funciona se self.navigationController for apresentado modalmente, mas self não for igual a self.navigationController.viewControllers [0], nesse caso self é pressionado.

Aqui está como você pode resolver o problema.

return self.presentingViewController.presentedViewController == self
            || (self.navigationController != nil && self.navigationController.presentingViewController.presentedViewController == self.navigationController && self == self.navigationController.viewControllers[0])
            || [self.tabBarController.presentingViewController isKindOfClass:[UITabBarController class]];

E em Swift:

return self.presentingViewController?.presentedViewController == self
        || (self.navigationController != nil && self.navigationController?.presentingViewController?.presentedViewController == self.navigationController && self.navigationController?.viewControllers[0] == self)
        || self.tabBarController?.presentingViewController is UITabBarController

6

Isso deve funcionar.

if(self.parentViewController.modalViewController == self)…

Infelizmente, isso não funciona. Foi minha primeira tentativa. Mas retornou modalViewController em nil :(.
morno

Se você apenas obter 'self.parentViewController', ele retorna o objeto pai correto?
kubi de

4
O problema pode ser que sua subclasse UIViewController está dentro de um UINavigationController ou um UITabBarController (ou ambos), caso em que você pode precisar cavar um pouco mais na hierarquia de visualização para descobrir o pai que foi apresentado como um controlador de visualização modal.
hpique de

@hgpc Eu precisava desse chck em meu projeto, então acabei de adicionar uma resposta para verificar os casos UINavigationControllere UITabBarController. Está funcionando muito bem até agora
Felipe Sabino

4

Melhor maneira de verificar

 if (self.navigationController.presentingViewController) {
         NSLog(@"Model Present");
    }

2

Se você não precisa distinguir entre visualizações modais de tela cheia e visualizações não modais, que é o caso do meu projeto (eu estava lidando com um problema que ocorre apenas com folhas de formulário e folhas de página), você pode usar o método modalPresentationStyle propriedade de UIViewController:

switch (self.modalPresentationStyle) {
    case 0: NSLog(@"full screen, or not modal"); break;
    case 1: NSLog(@"page sheet"); break;
    case 2: NSLog(@"form sheet"); break;
}

2

Em Swift :

func isUIViewControllerPresentedAsModal() -> Bool {
    if((self.presentingViewController) != nil) {
        return true
    }

    if(self.presentingViewController?.presentedViewController == self) {
        return true
    }

    if(self.navigationController?.presentingViewController?.presentedViewController == self.navigationController) {
        return true
    }

    if((self.tabBarController?.presentingViewController?.isKindOfClass(UITabBarController)) != nil) {
        return true
    }

    return false
}

Há um problema com este caso de uso. Se eu estiver em um controlador de visualização raiz de um UINavigationController, ele ainda retorna verdadeiro sem qualquer apresentação modal.
mariusLAN

1
A primeira instrução if cobre tudo o que está na segunda instrução if, tornando a segunda instrução redundante. Não tenho certeza de qual é a intenção aqui.
isoiphone

1

Em meu projeto, tenho um controlador de visualização (Detalhe) que pode ser apresentado modalmente (ao adicionar um novo item) ou com push (ao editar um existente) pelo controlador de visualização Master. Quando o usuário toca em [Concluído], o controlador de visualização de detalhes chama o método do controlador de visualização Mestre para notificar que está pronto para ser fechado. O Mestre deve determinar como o Detalhe é apresentado para saber como fechá-lo. É assim que eu faço isso:

UIViewController *vc = self.navigationController.viewControllers.lastObject;
if (vc == self) {
    [self dismissViewControllerAnimated:YES completion:NULL];
} else {
    [self.navigationController popViewControllerAnimated:YES];
}

0

Um hack como este pode funcionar.

UIViewController* child = self;
UIViewController* parent = child.parentViewController;
while (parent && parent.modalViewController != child) {
    child = parent;
    parent = child.parentViewController;
}
if (parent) {
    // A view controller in the hierarchy was presented as a modal view controller
}

No entanto, acho que minha resposta anterior é uma solução mais limpa.


0

O que funcionou para mim é o seguinte:

// this is the trick: set parent view controller as application's window root view controller
UIApplication.sharedApplication.delegate.window.rootViewController = viewController;

// assert no modal view is presented
XCTAssertNil(viewController.presentedViewController);

// simulate button tap which shows modal view controller
[viewController.deleteButton sendActionsForControlEvents:UIControlEventTouchUpInside];

// assert that modal view controller is presented
XCTAssertEqualObjects(viewController.presentedViewController.class, MyModalViewController.class);

Pelo que testei, funciona para iOS7 e iOS8. Porém, não tentei no iOS6.


0

Eu olhei um pouco ao redor para encontrar a resposta certa para esta pergunta, e não consegui encontrar nenhuma que cobrisse todos os cenários possíveis. Eu escrevi essas poucas linhas de código que parecem fazer o trabalho. Você pode encontrar alguns comentários embutidos para descobrir o que foi verificado.

- (BOOL)isModal {
    BOOL modal = NO;
    if ([self presentingViewController]) { //Some view Controller is presenting the current stack
        UIViewController *presented = [[self presentingViewController] presentedViewController]; // What's been presented
        if ([presented respondsToSelector:@selector(viewControllers)]) { // There's a stack
            NSArray *viewControllers = [presented performSelector:@selector(viewControllers)];
            modal = [viewControllers firstObject] == self; // Current VC is presented modally if it's the first in the stack
        }
        else {
            modal = presented == self; // Don't think this is actually needed. set modal = YES should do the job tho.
        }
    }
    return modal;
}

Espero que esta ajuda.


0

Aqui está minha versão modificada de @GabrielePetronella isModal, que funciona com controladores de visualização contidos no sentido de que sobe primeiro na hierarquia parentViewController. Também extraiu o código em várias linhas para que fique claro o que está fazendo.

var isModal: Bool {
    // If we are a child view controller, we need to check our parent's presentation
    // rather than our own.  So walk up the chain until we don't see any parentViewControllers
    var potentiallyPresentedViewController : UIViewController = self
    while (potentiallyPresentedViewController.parentViewController != nil) {
        potentiallyPresentedViewController = potentiallyPresentedViewController.parentViewController!
    }

    if self.presentingViewController?.presentedViewController == potentiallyPresentedViewController {
        return true
    }

    if let navigationController = potentiallyPresentedViewController.navigationController {
        if navigationController.presentingViewController?.presentedViewController == navigationController {
            return true
        }
    }

    return potentiallyPresentedViewController.tabBarController?.presentingViewController is UITabBarController
}
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.