Como saber se a visualização do UIViewController está visível

570

Eu tenho um aplicativo de barra de guias, com muitas visualizações. Existe uma maneira de saber se um determinado UIViewControlleritem está visível no momento UIViewController? (à procura de um imóvel)

Rob Bonner
fonte
Relacionado: Obtendo o melhor UIViewController
Sensuous

Respostas:

1098

A propriedade window da view é nula se uma view estiver visível no momento; portanto, verifique a view principal no controlador de view:

A chamada do método de exibição faz com que a exibição seja carregada (se não estiver carregada), o que é desnecessário e pode ser indesejável. Seria melhor verificar primeiro para ver se ele já está carregado. Eu adicionei a chamada ao isViewLoaded para evitar esse problema.

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

Desde o iOS9, ficou mais fácil:

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

Ou, se você tiver um UINavigationController gerenciando os controladores de exibição, poderá verificar sua propriedade visibleViewController .

progrmr
fonte
11
O único problema com a propriedade visibleViewControllee de um UINavigationController é o caso em que o visibleViewController apresenta um controlador de exibição modal. Nesse caso, a visualização modal se torna o visibleViewController, o que pode ser indesejável. Como você lidaria com isso?
Moshe
12
Este é provavelmente óbvio para todos, mas para mim o código tinha que ser self.isViewLoaded && self.view.window
JeffB6688
85
Tenha cuidado ao generalizar esta solução para outras situações. Por exemplo, se você estiver usando um UIPageViewController, as exibições de UIViewControllers que não são a página atual ainda poderão ter uma propriedade de janela nula porque elas serão renderizadas fora da tela. Nesse caso, consegui criar minha própria propriedade 'isCurrentlyVisible', que é definida em viewDidAppear e viewDidDisappear.
precisa saber é o seguinte
4
@ Moshe, nesse caso, use topViewController.
ma11hew28
3
Observe que esta resposta não diz nada sobre a visibilidade real. Por exemplo, se o aplicativo estiver em segundo plano acima da instrução SE, será exibido SIM enquanto a visualização não é realmente visível.
Marek J.
89

Aqui está a solução do @ progrmr como uma UIViewControllercategoria:

// 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
fonte
47

Existem alguns problemas com as soluções acima. Se você estiver usando, por exemplo, a UISplitViewController, a visualização principal sempre retornará verdadeira para

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

Em vez disso, adote esta abordagem simples que parece funcionar bem na maioria, se não em todos os 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
fonte
1
Isso ainda é verdade no xCode 7.1.1? O mestre no meu UISplitViewController está retornando NO para viewController.view.window. Posso estar fazendo algo errado, mas tenho certeza de que esse é o caso.
SAHM
44

Para aqueles que procuram uma versão Swift 2.2 da resposta:

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

e Swift 3 :

if self.isViewLoaded && (self.view.window != nil) {
         // viewController is visible
}
Benjamin
fonte
Não sei por que, mas descobri que fazer self.view.window! = Nil faz com que nunca funcione mesmo quando self.isViewLoaded for verdadeiro. Uma vez removido, ele funciona bem.
Micah Montoya
isso só funcionou para mim no viewDidAppear. ! Quando eu adicionei isso para viewWillAppear self.view.window = nil sempre veio nil
Lance Samaria
29

Para apresentações modais em tela cheia ou em contexto, "é visível" pode significar que está no topo da pilha do controlador de exibição ou apenas visível, mas coberto por outro controlador de exibição.

Para verificar se o controlador de visualização "é o controlador de visualização superior" é bastante diferente de "está visível", verifique a pilha do controlador de visualização do controlador de navegação do controlador de visualização.

Eu escrevi um pedaço de código para resolver esse 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
fonte
Bela postagem! A FYI isViewLoadedé uma propriedade desde o Swift 3.0.
precisa saber é o seguinte
28

Você quer usar o UITabBarController's selectedViewControllerpropriedade. Todos os controladores de visualização conectados a um controlador de barra de guias têm um tabBarControllerconjunto de propriedades, para que você possa, de qualquer código do controlador de visualização:

if([[[self tabBarController] selectedViewController] isEqual:self]){
     //we're in the active controller
}else{
     //we are not
}
executor21
fonte
2
Isso não funciona se o controlador de exibição estiver dentro de um controlador de navegação e esse controlador for adicionado ao controlador da barra de guias. A chamada para selectedViewController retornará o controlador de navegação e não o controlador de exibição atual.
Anton Holmberg
2
@AntonHolmberg, nesse caso, obtenha o controlador de exibição visível como este:((UINavigationController *)self.tabBarController.selectedViewController).visibleViewController
ma11hew28
Ou use a propriedade 'self.tabBarController.selectedIndex' se tivermos ido tão longe.
Vladimir Shutyuk 16/01
12

Fiz uma extensão rápida com base na resposta do @ progrmr.

Ele permite que você verifique facilmente se a UIViewControllerestá na tela assim:

if someViewController.isOnScreen {
    // Do stuff here
}

A extensão:

//
//  UIViewControllerExtension.swift
//

import UIKit

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

Para meus propósitos, no contexto de um controlador de exibição de contêiner, descobri que

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

funciona bem.

Chris Prince
fonte
3

se você estiver utilizando um UINavigationController e também quiser lidar com modos de exibição modais, é o que eu 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
fonte
2
Eu descobri que essa maneira é mais confiável do que a resposta aceita quando um controlador de navegação está disponível. Isso pode ser reduzido para: if ([self.navigationController.visibleViewController isKindOfClass: [self class]])] {
Darren
3

A abordagem que usei para um controlador de exibição apresentado modal foi verificar a classe do controlador apresentado. Se o controlador de exibição apresentado fosse ViewController2, eu executaria algum código.

UIViewController *vc = [self presentedViewController];

if ([vc isKindOfClass:[ViewController2 class]]) {
    NSLog(@"this is VC2");
}
agitação
fonte
3

Eu encontrei essas funções em 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);

Talvez as funções acima possam detectar a ViewControlleraparência ou não.

AechoLiu
fonte
3

XCode 6.4, para iOS 8.4, habilitado para ARC

Obviamente, muitas maneiras de fazê-lo. O que funcionou para mim é o seguinte ...

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

Isso pode ser usado em qualquer controlador de exibição da seguinte maneira,

[self.view.window isKeyWindow]

Se você chamar essa propriedade, -(void)viewDidLoadobtém 0, e depois -(void)viewDidAppear:(BOOL)animatedchamar 1.

Espero que isso ajude alguém. Obrigado! Felicidades.

serge-k
fonte
3

Se você estiver usando um controlador de navegação e apenas quiser saber se está no controlador ativo e superior , use:

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

Esta resposta é baseada no comentário de @mattdipasquale .

Se você tiver um cenário mais complicado, consulte as outras respostas acima.

phatmann
fonte
isso nunca será chamado se o aplicativo entrar em segundo plano e depois em primeiro plano. Estou procurando uma solução em que possa verificar se o controlador de exibição está visível para o usuário ou não. O usuário pode colocar o aplicativo em segundo plano por alguns dias e, quando voltar em primeiro plano, eu gostaria de atualizar a interface do usuário. Por favor, deixe-me saber se você pode ajudar.
Bibscy # 28/18
2

você pode verificá-lo por windowpropriedade

if(viewController.view.window){

// view visible

}else{

// no visible

}
Saad Ur Rehman
fonte
0

Eu precisava disso para verificar se o controlador de exibição é o atual controlador visualizado, fiz isso verificando se há algum controlador de exibição apresentado ou enviado pelo navegador, estou publicando-o no caso de alguém precisar dessa solução:

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

Eu uso essa pequena extensão no Swift 5 , que mantém simples e fácil verificar qualquer objeto que seja membro do UIView .

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

Então, eu apenas o uso como uma verificação simples de instrução if ...

if myView.isVisible {
    // do something
}

Espero que ajude! :)

valbu17
fonte