Cómo saber si la vista de UIViewController es visible

Respuestas:

1098

La propiedad de ventana de la vista no es nula si una vista está actualmente visible, así que verifique la vista principal en el controlador de vista:

Invocar el método de vista hace que la vista se cargue (si no está cargada), lo cual es innecesario y puede ser indeseable. Sería mejor verificar primero para ver si ya está cargado. Agregué la llamada a isViewLoaded para evitar este problema.

if (viewController.isViewLoaded && viewController.view.window) {
    // viewController is visible
}

Desde iOS9 se ha vuelto más fácil:

if viewController.viewIfLoaded?.window != nil {
    // viewController is visible
}

O si tiene un UINavigationController que administra los controladores de vista, puede verificar su propiedad visibleViewController en su lugar.

programador
fuente
11
El único problema con la propiedad visibleViewControllee de UINavigationController es el caso en que visibleViewController presenta un controlador de vista modal. En ese caso, la vista modal se convierte en visibleViewController, lo que puede ser indeseable. ¿Cómo manejarías eso?
Moshe
12
Esto probablemente sea obvio para todos, pero para mí el código tenía que ser self.isViewLoaded && self.view.window
JeffB6688
86
Tenga cuidado al generalizar esta solución a otras situaciones. Por ejemplo, si está utilizando un UIPageViewController, las vistas para UIViewControllers que no son la página actual pueden tener una propiedad de ventana no nula porque se muestran fuera de la pantalla. En este caso, he tenido éxito al hacer mi propia propiedad 'isCurrentlyVisible' que se establece en viewDidAppear y viewDidDisappear.
evanflash
44
@Moshe en ese caso, úsalo topViewController.
ma11hew28
3
Tenga en cuenta que esta respuesta no dice nada sobre la visibilidad real. Por ejemplo, si la aplicación está en segundo plano arriba, la instrucción IF dirá SÍ mientras la vista no es realmente visible.
Marek J.
89

Aquí está la solución de @progrmr como UIViewControllercategoría:

// UIViewController+Additions.h

@interface UIViewController (Additions)

- (BOOL)isVisible;

@end


// UIViewController+Additions.m

#import "UIViewController+Additions.h"

@implementation UIViewController (Additions)

- (BOOL)isVisible {
    return [self isViewLoaded] && self.view.window;
}

@end
ma11hew28
fuente
47

Hay un par de problemas con las soluciones anteriores. Si está utilizando, por ejemplo, a UISplitViewController, la vista maestra siempre devolverá verdadero para

if(viewController.isViewLoaded && viewController.view.window) {
    //Always true for master view in split view controller
}

En cambio, tome este enfoque simple que parece funcionar bien en la mayoría, si no en todos los casos:

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

    //We are now invisible
    self.visible = false;
}

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

    //We are now visible
    self.visible = true;
}
vincentjames501
fuente
1
¿Sigue siendo cierto en xCode 7.1.1? El maestro en mi UISplitViewController está devolviendo NO para viewController.view.window. Puede que esté haciendo algo mal, pero estoy bastante seguro de que este es el caso.
SAHM
44

Para aquellos de ustedes que buscan una versión Swift 2.2 de la respuesta:

if self.isViewLoaded() && (self.view.window != nil) {
     // viewController is visible
}

y Swift 3 :

if self.isViewLoaded && (self.view.window != nil) {
         // viewController is visible
}
Benjamín
fuente
No estoy seguro de por qué, pero descubrí que hacer self.view.window! = Nil hace que nunca funcione, incluso cuando self.isViewLoaded es verdadero. Una vez eliminado, funciona bien.
Micah Montoya
esto solo funcionó para mí en viewDidAppear. Cuando agregué esto a viewWillAppear self.view.window! = Nil siempre aparecía nil
Lance Samaria
29

Para presentaciones modales en pantalla completa o en contexto, "es visible" podría significar que está en la parte superior de la pila del controlador de vista o simplemente visible pero cubierto por otro controlador de vista.

Para verificar si el controlador de vista "es el controlador de vista superior" es bastante diferente de "es visible", debe verificar la pila del controlador de vista del controlador de navegación del controlador de vista.

Escribí un código para resolver este problema:

extension UIViewController {
    public var isVisible: Bool {
        if isViewLoaded {
            return view.window != nil
        }
        return false
    }

    public var isTopViewController: Bool {
        if self.navigationController != nil {
            return self.navigationController?.visibleViewController === self
        } else if self.tabBarController != nil {
            return self.tabBarController?.selectedViewController == self && self.presentedViewController == nil
        } else {
            return self.presentedViewController == nil && self.isVisible
        }
    }
}
WeZZard
fuente
¡Buen post! FYI isViewLoadedes una propiedad desde Swift 3.0.
Yuchen Zhong el
28

Desea usar la propiedad de UITabBarController's selectedViewController. Todos los controladores de vista conectados a un controlador de barra de pestañas tienen un tabBarControllerconjunto de propiedades, por lo que puede, desde cualquiera de los códigos de los controladores de vista:

if([[[self tabBarController] selectedViewController] isEqual:self]){
     //we're in the active controller
}else{
     //we are not
}
ejecutor21
fuente
2
Esto no funciona si el controlador de vista está contenido dentro de un controlador de navegación y ese controlador se agrega al controlador de la barra de pestañas. La llamada a selectedViewController devolverá el controlador de navegación y no el controlador de vista actual.
Anton Holmberg
2
@AntonHolmberg en ese caso, obtenga el controlador de vista visible de esta manera:((UINavigationController *)self.tabBarController.selectedViewController).visibleViewController
ma11hew28
O incluso use la propiedad 'self.tabBarController.selectedIndex' si hemos llegado tan lejos.
Vladimir Shutyuk
12

Hice una extensión rápida basada en la respuesta de @progrmr.

Le permite verificar fácilmente si hay un UIViewControlleren la pantalla de esta manera:

if someViewController.isOnScreen {
    // Do stuff here
}

La extensión:

//
//  UIViewControllerExtension.swift
//

import UIKit

extension UIViewController{
    var isOnScreen: Bool{
        return self.isViewLoaded() && view.window != nil
    }
}
Besi
fuente
7

Para mis propósitos, en el contexto de un controlador de vista de contenedor, he encontrado que

- (BOOL)isVisible {
    return (self.isViewLoaded && self.view.window && self.parentViewController != nil);
}

funciona bien.

Chris Prince
fuente
3

si está utilizando un UINavigationController y también desea manejar vistas modales, lo siguiente es lo que uso:

#import <objc/runtime.h>

UIViewController* topMostController = self.navigationController.visibleViewController;
if([[NSString stringWithFormat:@"%s", class_getName([topMostController class])] isEqualToString:@"NAME_OF_CONTROLLER_YOURE_CHECKING_IN"]) {
    //is topmost visible view controller
}
MrTristan
fuente
2
He encontrado que esta forma es más confiable que la respuesta aceptada, cuando hay un controlador de navegación disponible. Esto se puede acortar a: if ([self.navigationController.visibleViewController isKindOfClass: [self class]]) {
Darren
3

El enfoque que utilicé para un controlador de vista presentado modal fue verificar la clase del controlador presentado. Si el controlador de vista presentado fuera ViewController2entonces ejecutaría algún código.

UIViewController *vc = [self presentedViewController];

if ([vc isKindOfClass:[ViewController2 class]]) {
    NSLog(@"this is VC2");
}
meneando
fuente
3

Encontré esas funciones en UIViewController.h.

/*
  These four methods can be used in a view controller's appearance callbacks to determine if it is being
  presented, dismissed, or added or removed as a child view controller. For example, a view controller can
  check if it is disappearing because it was dismissed or popped by asking itself in its viewWillDisappear:
  method by checking the expression ([self isBeingDismissed] || [self isMovingFromParentViewController]).
*/

- (BOOL)isBeingPresented NS_AVAILABLE_IOS(5_0);
- (BOOL)isBeingDismissed NS_AVAILABLE_IOS(5_0);

- (BOOL)isMovingToParentViewController NS_AVAILABLE_IOS(5_0);
- (BOOL)isMovingFromParentViewController NS_AVAILABLE_IOS(5_0);

Tal vez las funciones anteriores pueden detectar si ViewControlleraparece o no.

AechoLiu
fuente
3

XCode 6.4, para iOS 8.4, ARC habilitado

Obviamente muchas formas de hacerlo. El que me ha funcionado es el siguiente ...

@property(nonatomic, readonly, getter=isKeyWindow) BOOL keyWindow

Esto se puede usar en cualquier controlador de vista de la siguiente manera,

[self.view.window isKeyWindow]

Si llama a esta propiedad -(void)viewDidLoadobtiene 0, entonces si llama a esto después de -(void)viewDidAppear:(BOOL)animatedobtener 1.

Espero que esto ayude a alguien. ¡Gracias! Salud.

serge-k
fuente
3

Si está utilizando un controlador de navegación y solo quiere saber si está en el controlador activo y superior , utilice:

if navigationController?.topViewController == self {
    // Do something
}

Esta respuesta se basa en el comentario de @mattdipasquale .

Si tiene un escenario más complicado, consulte las otras respuestas anteriores.

phatmann
fuente
esto nunca se llamará si la aplicación pasa a segundo plano y luego a primer plano. Estoy buscando una solución donde pueda verificar si el controlador de vista es visible para el usuario o no. El usuario puede poner en segundo plano la aplicación durante unos días y cuando vuelva a estar en primer plano, me gustaría actualizar la interfaz de usuario. Por favor, avíseme si puede ayudar.
bibscy
2

puedes consultarlo por windowpropiedad

if(viewController.view.window){

// view visible

}else{

// no visible

}
Saad Ur Rehman
fuente
0

Necesitaba esto para verificar si el controlador de vista es el controlador de vista actual, lo hice comprobando si hay algún controlador de vista presentado o empujado a través del navegador, lo estoy publicando en caso de que alguien necesite tal solución:

if presentedViewController != nil || navigationController?.topViewController != self {
      //Viewcontroller isn't viewed
}else{
     // Now your viewcontroller is being viewed 
}
Abdoelrhman
fuente
0

Utilizo esta pequeña extensión en Swift 5 , lo que hace que sea simple y fácil verificar cualquier objeto que sea miembro de UIView .

extension UIView {
    var isVisible: Bool {
        guard let _ = self.window else {
            return false
        }
        return true
    }
}

Entonces, solo lo uso como una simple comprobación de declaración if ...

if myView.isVisible {
    // do something
}

¡Espero que ayude! :)

valbu17
fuente