¿Es posible determinar si ViewController se presenta como modal?

Respuestas:

96

Dado que modalViewControllerha quedado obsoleto en iOS 6, aquí hay una versión que funciona para iOS 5+ y que se compila sin advertencias.

C objetivo:

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

Punta de sombrero a la respuesta de Felipe.

Gabriele Petronella
fuente
2
buena captura, solo tuve que usarlo nuevamente después de mucho tiempo y noté que sucedió la desaprobación ... Edité mi respuesta para que la gente comience a buscar aquí el código correcto cuando usa iOS 6+, gracias
Felipe Sabino
10
No funciona si el controlador de vista principal es un modal en el que se inserta nuestro controlador de vista.
significado importa
2
Hay un error, deberíamos comprobar si ambos lados son nulos, porque nil == nildevuelve YES, y no es el resultado que queremos.
CocoaBob
1
@GabrielePetronella ¿Te importa si actualizo la respuesta para incluir también una implementación Swift del método?
Michael Waterfall
1
@MichaelWaterfall sería muy apreciado, gracias
Gabriele Petronella
77

Si está buscando iOS 6+, esta respuesta está obsoleta y debe verificar la respuesta de Gabriele Petronella


No hay una forma sencilla de hacerlo, como propiedad o método nativo de UIKit. Lo que puede hacer es verificar varios aspectos de su controlador para asegurarse de que se presente como modal.

Entonces, para verificar si el controlador actual (representado como selfen el código a continuación) se presenta de una manera modal o no, tengo la función a continuación en una UIViewControllercategoría o (si su proyecto no necesita usar otros controladores UIKit, como UITableViewControllerpor ejemplo) en un controlador base que heredan mis otros controladores

-(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;        

}

EDITAR: agregué la última verificación para ver si se está utilizando un UITabBarController, y presenta otro UITabBarController como modal.

EDIT 2: se agregó la verificación de iOS 5+, donde UIViewControllerya no responde parentViewController, sino a presentingViewController.

EDITAR 3: He creado una esencia para ello por si acaso https://gist.github.com/3174081

Felipe Sabino
fuente
Tenga en cuenta que la modalViewControllerpropiedad está obsoleta a partir de iOS 6. La documentación sugiere utilizarla presentedViewControlleren su lugar.
Bart Jacobs
@BartJacobs ¡buen punto! No he visto esta respuesta después del lanzamiento de iOS6, por lo que es posible que no esté actualizada. Intentaré hacer algunas pruebas más adelante en la semana para actualizarlo, ¡tks!
Felipe Sabino
NSLog(@"%@", self.navigationController.parentViewController)impresiones (null)- ¿podría explicar por qué? My ViewController está conectado con el controlador de vista modal a través de navController en el guión gráfico.
Roman
@oyatek ¿puedes usar pastebin o algo similar y mostrar algún código?
Felipe Sabino
@Feilpe Encontré el problema: .parentViewControllerestá en desuso, .presentingViewControllerdebe usarse en su lugar.
Roman
35

En iOS5 +, como puede ver en UIViewController Class Reference , puede obtenerlo de la propiedad "presentationViewController".

presentViewController El controlador de vista que presentó este controlador de vista. (solo lectura)

@property (no atómico, solo lectura) UIViewController * PresentandoViewController
Discusión

Si el controlador de vista que recibió este mensaje es presentado por otro controlador de vista, esta propiedad contiene el controlador de vista que lo presenta. Si no se presenta el controlador de vista, pero se presenta uno de sus antepasados, esta propiedad contiene el controlador de vista que presenta el antepasado más cercano. Si no se presentan ni el controlador de vista ni ninguno de sus antepasados, esta propiedad es nula.

Disponibilidad
Disponible en iOS 5.0 y posterior.
Declarado en
UIViewController.h

Raj
fuente
3
Funciona perfectamente, use if (self.presentingViewController) {// Este es un viewContoller modal} else {// Este es un ViewController normal}
mashdup
2
En mi humilde opinión, esta es la única respuesta correcta aquí. Solo verifique la presencia de un presentingViewController. También funcionará en controladores de vista de contenedor, ya que atraviesa automáticamente los antepasados.
Daniel Rinser
17

Si no lo hay, puede definir una propiedad para esto (presentedAsModal ) en su subclase UIViewController y establecerla en YESantes de presentar ViewController como una vista modal.

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

Puede comprobar este valor en su viewWillAppearanulación.

Creo que no hay una propiedad oficial que indique cómo se presenta la vista, pero nada le impide crear la suya propia.

hpique
fuente
Bien, y esto es lo que hice, pero estaba buscando otra solución ordenada. Gracias.
Tiburón
esta solución no funciona si presenta un UINavigationControllercomo modal ... a menos que cree un controlador de navegación personalizado solo para agregar esta propiedad. Y después de eso, dentro de los controladores, tendrás que seguir transmitiendo self.navigationControllera esta clase personalizada cada vez que necesites verificar si el controlador se presenta como modal
Felipe Sabino
8

La respuesta de Petronella no funciona si self.navigationController se presenta de forma modal pero self no es igual a self.navigationController.viewControllers [0], en ese caso se empuja self.

Así es como puede solucionar el 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]];

Y en 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
fuente
6

Esto debería funcionar.

if(self.parentViewController.modalViewController == self)…
kubi
fuente
Por desgracia, esto no funciona. Fue mi primer intento. Pero devolvió modalViewController ins nil :(.
lukewar
Si solo obtiene 'self.parentViewController', ¿devuelve el objeto principal correcto?
kubi
4
El problema podría ser que su subclase UIViewController está dentro de un UINavigationController o un UITabBarController (o ambos), en cuyo caso es posible que deba profundizar un poco más en la jerarquía de vistas para averiguar el padre que se presentó como un controlador de vista modal.
hpique
@hgpc que necesitaba este chck en mi proyecto, por lo que me acaba de agregar una respuesta para comprobar si hay tanto UINavigationControllery UITabBarControllercasos. Está funcionando bastante bien hasta ahora
Felipe Sabino
4

La mejor forma de comprobar

 if (self.navigationController.presentingViewController) {
         NSLog(@"Model Present");
    }
Sunny Shah
fuente
2

Si no necesita distinguir entre vistas modales de pantalla completa y vistas no modales, que es el caso en mi proyecto (estaba lidiando con un problema que solo ocurre con hojas de formulario y hojas de página), puede usar modalPresentationStyle propiedad 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;
}
arlomedia
fuente
2

En 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
}
Rey Mago
fuente
Hay un problema con este caso de uso. Si estoy en un controlador de vista raíz de un UINavigationController, todavía devuelve verdadero sin ninguna presentación modal.
mariusLAN
1
La primera instrucción if cubre todo lo que está en la segunda instrucción if, lo que hace que la segunda instrucción sea redundante. No estoy seguro de cuál es la intención aquí.
isoiphone
1

En mi proyecto tengo un controlador de vista (Detalle) que se puede presentar de forma modal (al agregar un nuevo elemento) o con push (al editar uno existente) mediante el controlador de vista maestro. Cuando el usuario toca [Listo], el controlador de vista de detalles llama al método del controlador de vista maestro para notificar que está listo para cerrarse. El maestro tiene que determinar cómo se presenta Detail para saber cómo cerrarlo. Así es como hago esto:

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

Un truco como este podría 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
}

Sin embargo, creo que mi respuesta anterior es una solución más limpia.

hpique
fuente
0

Lo que funcionó para mí es lo siguiente:

// 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);

Por lo que lo probé, esto funciona para iOS7 e iOS8. Sin embargo, no probé en iOS6.

mixtly87
fuente
0

Miré un poco a mi alrededor para encontrar la respuesta correcta a esta pregunta y no pude encontrar ninguna que cubriera todos los escenarios posibles. Escribí estas pocas líneas de código que parecen hacer el trabajo. Puede encontrar algunos comentarios en línea para averiguar qué se ha 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 esto ayude.

DennyLou
fuente
0

Aquí está mi versión modificada de @ GabrielePetronella isModal, que funciona con controladores de vista contenidos, ya que primero sube por la jerarquía parentViewController. También extrajo el código en varias líneas para que quede claro lo que está haciendo.

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
fuente