Comment savoir si la vue de UIViewController est visible

570

J'ai une application de barre d'onglets, avec de nombreuses vues. Existe-t-il un moyen de savoir si un particulier UIViewControllerest actuellement visible de l'intérieur du UIViewController? (à la recherche d'une propriété)

Rob Bonner
la source

Réponses:

1098

La propriété window de la vue n'est pas nulle si une vue est actuellement visible, alors vérifiez la vue principale dans le contrôleur de vue:

L'appel de la méthode view provoque le chargement de la vue (si elle n'est pas chargée), ce qui est inutile et peut être indésirable. Il serait préférable de vérifier d'abord s'il est déjà chargé. J'ai ajouté l'appel à isViewLoaded pour éviter ce problème.

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

Depuis iOS9, il est devenu plus facile:

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

Ou si vous avez un UINavigationController gérant les contrôleurs de vue, vous pouvez vérifier sa propriété visibleViewController à la place.

progrmr
la source
11
Le seul problème avec la propriété visibleViewControllee d'UINavigationController est le cas où votre visibleViewController présente un contrôleur de vue modale. Dans ce cas, la vue modale devient visibleViewController, ce qui peut être indésirable. Comment géreriez-vous cela?
Moshe
12
C'est probablement évident pour tout le monde, mais pour moi, le code devait être self.isViewLoaded && self.view.window
JeffB6688
86
Soyez prudent en généralisant cette solution à d'autres situations. Par exemple, si vous utilisez un UIPageViewController, les vues des UIViewControllers qui ne sont pas la page actuelle peuvent toujours avoir une propriété de fenêtre non nulle car elles sont rendues hors écran. Dans ce cas, j'ai réussi à créer ma propre propriété «isCurrentlyVisible» qui est définie dans viewDidAppear et viewDidDisappear.
evanflash du
4
@Moshe dans ce cas, utilisez topViewController.
ma11hew28
3
Veuillez noter que cette réponse ne dit rien sur la visibilité réelle. Par exemple, si l'application est en arrière-plan au-dessus de l'instruction IF, vous direz OUI alors que la vue n'est pas vraiment visible.
Marek J.15
89

Voici la solution de @ progrmr en tant que UIViewControllercatégorie:

// 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
la source
47

Il y a quelques problèmes avec les solutions ci-dessus. Si vous utilisez, par exemple, a UISplitViewController, la vue principale retournera toujours true pour

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

Au lieu de cela, adoptez cette approche simple qui semble bien fonctionner dans la plupart, sinon tous les cas:

- (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
la source
1
Est-ce toujours vrai dans xCode 7.1.1? Le maître de mon UISplitViewController renvoie NON pour viewController.view.window. Je fais peut-être quelque chose de mal, mais je suis presque sûr que c'est le cas.
SAHM
44

Pour ceux d'entre vous qui recherchent une version Swift 2.2 de la réponse:

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

et Swift 3 :

if self.isViewLoaded && (self.view.window != nil) {
         // viewController is visible
}
Benjamin
la source
Je ne sais pas pourquoi mais j'ai trouvé que faire self.view.window! = Nil l'empêche de fonctionner même lorsque self.isViewLoaded est vrai. Une fois retiré, cela fonctionne bien.
Micah Montoya
cela n'a fonctionné que pour moi dans viewDidAppear. Quand j'ai ajouté ceci à viewWillAppear self.view.window! = Nil est toujours venu nil
Lance Samaria
29

Pour une présentation modale sur plein écran ou sur contexte, "est visible" peut signifier qu'il est au-dessus de la pile du contrôleur de vue ou juste visible mais couvert par un autre contrôleur de vue.

Pour vérifier si le contrôleur de vue "est le contrôleur de vue de dessus" est assez différent de "est visible", vous devez vérifier la pile de contrôleur de vue du contrôleur de navigation du contrôleur de vue.

J'ai écrit un morceau de code pour résoudre ce problème:

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
la source
Belle publication! FYI isViewLoadedest une propriété depuis Swift 3.0.
Yuchen Zhong
28

Vous voulez utiliser le UITabBarControllerde » selectedViewControllerla propriété. Tous les contrôleurs de vue attachés à un contrôleur de barre d'onglets ont un tabBarControllerjeu de propriétés, vous pouvez donc, à partir de n'importe quel code des contrôleurs de vue:

if([[[self tabBarController] selectedViewController] isEqual:self]){
     //we're in the active controller
}else{
     //we are not
}
exécuteur21
la source
2
Cela ne fonctionne pas si le contrôleur de vue est contenu dans un contrôleur de navigation et que ce contrôleur est ajouté au contrôleur de la barre d'onglets. L'appel à selectedViewController renverra le contrôleur de navigation et non le contrôleur de vue actuel.
Anton Holmberg
2
@AntonHolmberg dans ce cas, obtenez le contrôleur de vue visible comme ceci:((UINavigationController *)self.tabBarController.selectedViewController).visibleViewController
ma11hew28
Ou même utilisez la propriété «self.tabBarController.selectedIndex» si nous sommes allés aussi loin.
Vladimir Shutyuk
12

J'ai fait une extension rapide basée sur la réponse de @ progrmr.

Il vous permet de vérifier facilement si un UIViewControllerest à l'écran comme ceci:

if someViewController.isOnScreen {
    // Do stuff here
}

L'extension:

//
//  UIViewControllerExtension.swift
//

import UIKit

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

Pour mes besoins, dans le contexte d'un contrôleur de vue de conteneur, j'ai trouvé que

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

fonctionne bien.

Chris Prince
la source
3

si vous utilisez un UINavigationController et souhaitez également gérer les vues modales, voici ce que j'utilise:

#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
la source
2
J'ai trouvé cette façon d'être plus fiable que la réponse acceptée, lorsqu'un contrôleur de navigation est disponible. Cela peut être raccourci en: if ([self.navigationController.visibleViewController isKindOfClass: [self class]]) {
Darren
3

L'approche que j'ai utilisée pour un contrôleur de vue présenté modal était de vérifier la classe du contrôleur présenté. Si le contrôleur de vue présenté l'était ViewController2, j'exécuterais du code.

UIViewController *vc = [self presentedViewController];

if ([vc isKindOfClass:[ViewController2 class]]) {
    NSLog(@"this is VC2");
}
réprimande
la source
3

J'ai trouvé ces fonctions 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);

Peut-être que les fonctions ci-dessus peuvent détecter l' ViewControllerapparition ou non.

AechoLiu
la source
3

XCode 6.4, pour iOS 8.4, compatible ARC

De toute évidence, de nombreuses façons de le faire. Celui qui a fonctionné pour moi est le suivant ...

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

Cela peut être utilisé dans n'importe quel contrôleur de vue de la manière suivante,

[self.view.window isKeyWindow]

Si vous appelez cette propriété, -(void)viewDidLoadvous obtenez 0, alors si vous appelez cela après avoir -(void)viewDidAppear:(BOOL)animatedobtenu 1.

J'espère que cela aide quelqu'un. Merci! À votre santé.

serge-k
la source
3

Si vous utilisez un contrôleur de navigation et que vous voulez simplement savoir si vous êtes dans le contrôleur actif et le plus haut , utilisez alors:

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

Cette réponse est basée sur le commentaire de @mattdipasquale .

Si vous avez un scénario plus compliqué, consultez les autres réponses ci-dessus.

phatmann
la source
cela ne sera jamais appelé si l'application passe en arrière-plan puis au premier plan. Je cherche une solution où je peux vérifier si le contrôleur de vue est visible pour l'utilisateur ou non. L'utilisateur peut mettre l'application en arrière-plan pendant quelques jours et lorsqu'elle reviendra au premier plan, je voudrais mettre à jour l'interface utilisateur. Veuillez me faire savoir si vous pouvez m'aider.
bibscy
2

vous pouvez le vérifier par windowpropriété

if(viewController.view.window){

// view visible

}else{

// no visible

}
Saad Ur Rehman
la source
0

J'en avais besoin pour vérifier si le contrôleur de vue est le contrôleur actuellement affiché, je l'ai fait en vérifiant s'il y a un contrôleur de vue présenté ou poussé via le navigateur, je le poste au cas où quelqu'un aurait besoin d'une telle solution:

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

J'utilise cette petite extension dans Swift 5 , ce qui le rend simple et facile à vérifier pour tout objet qui est membre de UIView .

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

Ensuite, je l'utilise comme simple vérification de l'instruction if ...

if myView.isVisible {
    // do something
}

J'espère que ça aide! :)

valbu17
la source