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

117

É possível verificar dentro da classe ViewController se ela é apresentada como controlador de visualização modal?

morno
fonte

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.

Gabriele Petronella
fonte
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

Felipe sabino
fonte
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

Raj
fonte
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.

hpique
fonte
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
Semih Cihan
fonte
6

Isso deve funcionar.

if(self.parentViewController.modalViewController == self)…
kubi
fonte
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");
    }
Sunny Shah
fonte
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;
}
arlomédia
fonte
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
}
O rei da bruxaria
fonte
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];
}
Olex
fonte
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.

hpique
fonte
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.

mixtly87
fonte
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.

DennyLou
fonte
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
}
Ryan
fonte