Jak stwierdzić, czy widok UIViewController jest widoczny

Odpowiedzi:

1097

Właściwość okna widoku jest różna od zera, jeśli widok jest obecnie widoczny, więc sprawdź widok główny w kontrolerze widoku:

Wywołanie metody widoku powoduje załadowanie widoku (jeśli nie jest załadowany), co jest niepotrzebne i może być niepożądane. Lepiej najpierw sprawdzić, czy jest już załadowany. Dodałem wywołanie do isViewLoaded, aby uniknąć tego problemu.

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

Od iOS9 stało się łatwiejsze:

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

Lub jeśli masz UINavigationController zarządzający kontrolerami widoku, możesz zamiast tego sprawdzić jego właściwość visibleViewController .

progrmr
źródło
11
Jedynym problemem związanym z właściwością visibleViewControllee UINavigationController jest przypadek, w którym Twój visibleViewController przedstawia kontroler widoku modalnego. W takim przypadku widok modalny staje się widocznyViewController, co może być niepożądane. Jak sobie z tym poradzisz?
Moshe
12
Jest to prawdopodobnie oczywiste dla wszystkich, ale dla mnie kod musiał być self.isViewLoaded && self.view.window
JeffB6688
85
Zachowaj ostrożność przy uogólnianiu tego rozwiązania na inne sytuacje. Na przykład, jeśli używasz UIPageViewController, widoki dla UIViewControllers, które nie są bieżącą stroną, mogą nadal mieć inną właściwość okna, ponieważ są renderowane poza ekranem. W tym przypadku udało mi się stworzyć własną właściwość „isCurrentlyVisible”, która jest ustawiana w viewDidAppear i viewDidDisappear.
evanflash,
4
@Moshe w takim przypadku użyj topViewController.
ma11hew28
3
Pamiętaj, że ta odpowiedź nie mówi nic o prawdziwej widoczności. Na przykład, jeśli aplikacja jest w tle nad instrukcją IF powie TAK, podczas gdy widok nie jest tak naprawdę widoczny.
Marek J.
89

Oto rozwiązanie @ progrmr jako UIViewControllerkategorię:

// 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
źródło
47

Istnieje kilka problemów z powyższymi rozwiązaniami. Jeśli używasz na przykład a UISplitViewController, widok główny zawsze zwróci wartość true dla

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

Zamiast tego zastosuj to proste podejście, które wydaje się działać dobrze w większości, jeśli nie we wszystkich przypadkach:

- (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
źródło
1
Czy nadal jest to prawdą w xCode 7.1.1? Master w moim UISplitViewController zwraca NIE dla viewController.view.window. Mogę robić coś złego, ale jestem pewien, że tak jest.
SAHM
44

Dla tych z Was, którzy szukają odpowiedzi Swift 2.2 :

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

i Swift 3 :

if self.isViewLoaded && (self.view.window != nil) {
         // viewController is visible
}
Benzoes
źródło
Nie jestem pewien, dlaczego, ale odkryłem, że wykonanie self.view.window! = Zero powoduje, że nigdy nie będzie działać, nawet jeśli self.isViewLoaded jest prawdą. Po usunięciu działa dobrze.
Micheasz Montoya,
działało to tylko dla mnie w viewDidAppear. Kiedy dodałem to do viewWillAppear self.view.window! = Zero zawsze pojawiało się zero
Lance Samaria
29

W przypadku prezentacji modalnej na pełnym ekranie lub w kontekście, „widoczny” może oznaczać, że znajduje się na wierzchu stosu kontrolera widoku lub jest po prostu widoczny, ale zakryty przez inny kontroler widoku.

Aby sprawdzić, czy kontroler widoku „jest kontrolerem widoku z góry” różni się znacznie od „jest widoczny”, należy sprawdzić stos kontrolera widoku kontrolera nawigacji kontrolera widoku.

Napisałem kawałek kodu, aby rozwiązać ten problem:

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
źródło
Niezły post! FYI isViewLoadedjest własnością od Swift 3.0.
Yuchen Zhong
28

Chcesz korzystać z UITabBarController„s selectedViewControllernieruchomości. Wszystkie kontrolery widoku podłączone do kontrolera paska kart mają tabBarControllerzestaw właściwości, dzięki czemu możesz w dowolnym kodzie kontrolerów widoku:

if([[[self tabBarController] selectedViewController] isEqual:self]){
     //we're in the active controller
}else{
     //we are not
}
executor21
źródło
2
Nie działa to, jeśli kontroler widoku jest zawarty w kontrolerze nawigacyjnym i ten kontroler jest dodany do kontrolera paska kart. Połączenie z wybranym kontroleremView zwróci kontroler nawigacji, a nie bieżący kontroler widoku.
Anton Holmberg,
2
@AntonHolmberg w takim przypadku uzyskaj widoczny kontroler widoku w następujący sposób:((UINavigationController *)self.tabBarController.selectedViewController).visibleViewController
ma11hew28
Lub nawet użyj właściwości „self.tabBarController.selectedIndex”, jeśli zaszliśmy tak daleko.
Vladimir Shutyuk
12

Zrobiłem szybkie rozszerzenie w oparciu o odpowiedź @ progrmr.

Pozwala łatwo sprawdzić, czy UIViewControllerna ekranie jest taki:

if someViewController.isOnScreen {
    // Do stuff here
}

Rozszerzenie:

//
//  UIViewControllerExtension.swift
//

import UIKit

extension UIViewController{
    var isOnScreen: Bool{
        return self.isViewLoaded() && view.window != nil
    }
}
Besi
źródło
7

Dla moich celów, w kontekście kontrolera widoku kontenera, znalazłem to

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

działa dobrze.

Chris Prince
źródło
3

jeśli korzystasz z UINavigationController, a także chcesz obsługiwać widoki modalne, używam następujących opcji:

#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
źródło
2
Odkryłem, że ten sposób jest bardziej niezawodny niż zaakceptowana odpowiedź, gdy dostępny jest kontroler nawigacyjny. Można to skrócić do: if ([self.navigationController.visibleViewController isKindOfClass: [self class]]) {
Darren,
3

Podejście, które zastosowałem dla modalnego kontrolera widoku, polegało na sprawdzeniu klasy prezentowanego kontrolera. Gdyby prezentowany kontroler widoku był ViewController2, to wykonałbym trochę kodu.

UIViewController *vc = [self presentedViewController];

if ([vc isKindOfClass:[ViewController2 class]]) {
    NSLog(@"this is VC2");
}
bura
źródło
3

Znalazłem te funkcje w 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);

Być może powyższe funkcje mogą wykryć ViewControllerpojawienie się lub nie.

AechoLiu
źródło
3

XCode 6.4, dla iOS 8.4, włączony ARC

Oczywiście na wiele sposobów. Ten, który pracował dla mnie, jest następujący ...

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

Można tego użyć w dowolnym kontrolerze widoku w następujący sposób,

[self.view.window isKeyWindow]

Jeśli wywołasz tę właściwość -(void)viewDidLoad, otrzymasz 0, a następnie, jeśli wywołasz ją po -(void)viewDidAppear:(BOOL)animatedotrzymaniu 1.

Mam nadzieję, że to komuś pomoże. Dzięki! Twoje zdrowie.

serge-k
źródło
3

Jeśli korzystasz z kontrolera nawigacyjnego i chcesz tylko wiedzieć, czy korzystasz z aktywnego i najwyższego kontrolera, użyj:

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

Ta odpowiedź oparta jest na @mattdipasquale komentarzu .

Jeśli masz bardziej skomplikowany scenariusz, zobacz inne odpowiedzi powyżej.

phatmann
źródło
to nigdy nie zostanie wywołane, jeśli aplikacja przejdzie w tle, a następnie na pierwszym planie. Szukam rozwiązania, w którym mogę sprawdzić, czy kontroler widoku jest widoczny dla użytkownika, czy nie. Użytkownik może uruchomić aplikację w tle przez kilka dni, a gdy wróci na pierwszy plan, chciałbym zaktualizować interfejs użytkownika. Daj mi znać, jeśli możesz pomóc.
bibscy
2

możesz to sprawdzić według windowwłaściwości

if(viewController.view.window){

// view visible

}else{

// no visible

}
Saad Ur Rehman
źródło
0

Potrzebowałem tego, aby sprawdzić, czy kontroler widoku jest aktualnie oglądanym kontrolerem, zrobiłem to, sprawdzając, czy jest jakiś przedstawiony kontroler widoku lub przepchnąłem nawigatora, publikuję go na wypadek, gdyby ktoś potrzebował takiego rozwiązania:

if presentedViewController != nil || navigationController?.topViewController != self {
      //Viewcontroller isn't viewed
}else{
     // Now your viewcontroller is being viewed 
}
Abdoelrhman
źródło
0

Korzystam z tego małego rozszerzenia w Swift 5 , dzięki czemu można łatwo i łatwo sprawdzić dowolny obiekt będący członkiem UIView .

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

Następnie używam go jako prostego sprawdzania instrukcji if ...

if myView.isVisible {
    // do something
}

Mam nadzieję, że to pomoże! :)

valbu17
źródło