¿Cómo verificar si un controlador de vista se presenta modalmente o se presiona en una pila de navegación?

126

¿Cómo puedo, en mi código de controlador de vista, diferenciar entre:

  • presentado modalmente
  • empujado en la pila de navegación

Ambos presentingViewControllery isMovingToParentViewControllerson YESen ambos casos, por lo que no son de mucha ayuda.

Lo que complica las cosas es que mi controlador de vista principal es a veces modal, en el que se empuja el controlador de vista que se va a verificar.

Resulta que mi problema es que inserto mi HtmlViewControlleren uno UINavigationControllerque luego se presenta. Es por eso que mis propios intentos y las buenas respuestas a continuación no estaban funcionando.

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

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

Supongo que será mejor que le diga a mi controlador de vista cuando sea modal, en lugar de tratar de determinarlo.

el significado importa
fuente

Respuestas:

125

Tomar con un grano de sal, no probado.

- (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
fuente
12
Encontré esto en otra publicación SO. Pero, no funciona si el padre del controlador de vista push es modal; cual es la situación que estoy teniendo
significado-asuntos
2
Como escribí, presentingViewControllersiempre está YESen mi caso; no ayuda.
significado-asuntos
3
presentingViewControllervuelve YESpara VC empujado, cuando hay un UITabBarControllerser establecido como root. Por lo tanto, no es adecuado en mi caso.
Yevhen Dubinin
55
Esto no funciona si presenta un controlador de vista y luego empuja a otro.
Lee
3
"Esto no funciona si presenta un controlador de vista y luego empuja otro" Esa no es la intención de esto, el controlador de vista empujado no se presenta.
Colin Swelin
87

En Swift :

Agregue una bandera para probar si es modal por tipo de clase:

// 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
    }
}
Rey Mago
fuente
44
Debería ser mejor en una var, comovar isModal: Bool {}
malinois
1
@malinois ha cambiado
YannSteph
¿Qué hace el último falseparámetro en la returndeclaración?
damjandd
necesita un cambio para permitir presentarIsNavigation = navigationController? .presentingViewController? .presentedViewController == navigationController && navigationController! = nil
famfamfam
78

Usted pasa por alto un método: isBeingPresented.

isBeingPresented es verdadero cuando se presenta el controlador de vista y falso cuando se lo empuja.

- (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
fuente
2
Intenté esto también antes de publicar, y no funciona, isBeingPresentedes NO. Pero ahora veo la razón, estoy incrustando mi controlador de vista presentado en un UINavigationController, y ese es el que estoy presionando.
significado-asuntos
1
No puede presionar un controlador de navegación. Quizás quisiste decir que estás presentando el controlador de navegación.
rmaddy
3
@jowie Uso p, no poal imprimir un valor primitivo. poes para imprimir objetos.
rmaddy
37
Documentación para isBeingPresented: este método devuelve SÍ solo cuando se llama desde dentro de viewWillAppear: y viewDidAppear: métodos.
funct7
44
@Terrence Parece que la última documentación no muestra esa información, pero solía estar allí. El isBeingPresented, isBeingDismissed, isMovingFromParentViewControllery isMovingToParentViewControllerson válidas sólo dentro de los 4 view[Will|Did][Disa|A]ppearmétodos.
rmaddy
29

Swift 5
Aquí hay una solución que aborda el problema mencionado con las respuestas anteriores, cuando se isModal()devuelve truesi se presiona UIViewControllerestá en una UINavigationControllerpila presentada .

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

Funciona para mí hasta ahora. Si hay algunas optimizaciones, por favor comparta.

Jonauz
fuente
¿Por qué necesitas verificar tabBarController?.presentingViewController is UITabBarController ? ¿Importa si eso presentingViewControllertambién es un UITabBarController?
Hlung
Y si navigationController es nulo, isModalvolverá true. ¿Es esto intencionado?
Hlung
28

self.navigationController! = nil significaría que está en una pila de navegación.

Para manejar el caso de que el controlador de vista actual se presiona mientras el controlador de navegación se presenta modalmente, he agregado algunas líneas de código para verificar si el controlador de vista actual es el controlador raíz en la pila de navegación.

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
fuente
Bueno, en general, cuando presentas modalmente, pones el viewController en un navigationController y lo presentas. Si ese es el caso, su declaración sería incorrecta, sin embargo, en el código se maneja este caso. Por favor, mejora tu respuesta :)
E-Riddie
buen trabajo que se ocupa de todos los casos de uso. espacio para un poco de refactorización probablemente, pero aún así ¡¡¡upvote !!
Jean Raymond Daher
12

Swift 4

var isModal: Bool {
    return presentingViewController != nil ||
           navigationController?.presentingViewController?.presentedViewController === navigationController ||
           tabBarController?.presentingViewController is UITabBarController
}
Charlton Provatas
fuente
Swift 4.2 / iOS 12. Todavía funciona bien, pero tenga en cuenta que navigationController? .PresentingViewController? .PresentedViewController === navigationController evaluará a verdadero si ambos son nulos (por ejemplo, si lo llama en un controlador de vista que aún no ha sido presentado).
Eli Burke
7

Swift 5. Limpio y simple.

if navigationController.presentingViewController != nil {
    // Navigation controller is being presented modally
}
Kirill Kudaev
fuente
1
esto hizo el truco para mí
Radu Ursache
3

Como muchas personas aquí sugieren, que los métodos de "verificación" no funcionan bien en todos los casos, en mi proyecto se me ocurrió una solución para administrarlo manualmente. El punto es que, por lo general, gestionamos la presentación por nuestra cuenta; esto no es lo que sucede detrás de escena y debemos introspectarlo.

DEViewController.h expediente:

#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

Las presentaciones ahora se podrían gestionar de esta manera:

empujado en la pila de navegación:

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

presentado modalmente con navegación:

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

presentado modalmente:

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

Además, en DEViewControllerpodríamos agregar una alternativa a la "verificación" si la propiedad mencionada es 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
fuente
3

Suponiendo que todos los viewControllers que presentas modalmente están envueltos dentro de un nuevo navigationController (que siempre debes hacer de todos modos), puedes agregar esta propiedad a tu VC.

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

    return false
}
Demosthese
fuente
1
que siempre debes hacer de todos modos , ¿por qué explicar?
Alexander Abakumov
Alexander, no deberías, de verdad.
nickdnk
2

Para detectar que su controlador es presionado o no, simplemente use el siguiente código en el lugar que desee:

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

    // Not pushed
}
else {

    // Pushed
}

Espero que este código pueda ayudar a cualquiera ...

Arash Zeinoddini
fuente
1
Este método no funciona cuando usa la misma clase de controlador de vista en varios lugares, ya que solo verifica la clase de la misma. En su lugar, puede verificar explícitamente la igualdad.
gklka
1

self.navigationController != nil significaría que está en una pila de navegación.

Daniel
fuente
25
Todavía puede estar en un controlador de navegación modal
ColdLogic
Entonces 'modal' y 'empujado en la pila de navegación' no son mutuamente excluyentes. Pensar que esto depende del contexto, pero comprobar si self.navigationController no es nulo responde si se trata de un controlador de vista de un controlador de navegación.
Daniel
@Daniel La diferencia es entre "empujado" y "presentado". "Modal" no tiene nada que ver con eso. Creo que "ColdLogic" significa "presentado" cuando dijeron "modal".
rmaddy
1
if let navigationController = self.navigationController, navigationController.isBeingPresented {
    // being presented
}else{
    // being pushed
}
mkto
fuente
0

Si está utilizando ios 5.0 o posterior, utilice 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
fuente
0

Swift 5
Esta práctica extensión maneja pocos casos más que las respuestas anteriores. Estos casos son VC (controlador de vista) es el VC raíz de la ventana de la aplicación, VC se agrega como VC secundario a primario. Intenta devolver verdadero solo si el viewcontroller se presenta modalmente.

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

Gracias a la respuesta de Jonauz . Nuevamente hay espacio para más optimizaciones. Discuta sobre el caso que debe manejarse en la sección de comentarios.

Mehedi Hasan
fuente
-1

Para alguien que se pregunta, Cómo decirle a ViewController que se está presentando

si Aestá presentando / empujandoB

  1. Definir un enumy propertyenB

    enum ViewPresentationStyle {
        case Push
        case Present
    }
    
    //and write property 
    
    var vcPresentationStyle : ViewPresentationStyle = .Push //default value, considering that B is pushed 
  2. Ahora en el Acontrolador de vista, diga Bsi se está presentando / empujando asignandopresentationStyle

    func presentBViewController() {
        let bViewController = B()
        bViewController.vcPresentationStyle = .Present //telling B that it is being presented
        self.presentViewController(bViewController, animated: true, completion: nil)
    }
  3. Uso en el Bcontrolador de vista

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

Esto le permitirá saber si viewController se presenta o se empuja

iCoder86
fuente
44
Esta propiedad está en desuso.
Morkrom