Как определить, виден ли вид UIViewController

Ответы:

1098

Свойство окна представления не равно nil, если представление в настоящее время видно, поэтому проверьте основной вид в контроллере представления:

Вызов метода view приводит к загрузке представления (если оно не загружено), что является ненужным и может быть нежелательным. Было бы лучше сначала проверить, загружен ли он. Я добавил вызов isViewLoaded, чтобы избежать этой проблемы.

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

С iOS9 стало проще:

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

Или, если у вас есть UINavigationController, управляющий контроллерами представления, вы можете вместо этого проверить его свойство visibleViewController .

progrmr
источник
11
Единственная проблема со свойством visibleViewControllee в UINavigationController - это случай, когда ваш visibleViewController представляет модальный контроллер представления. В этом случае модальное представление становится visibleViewController, что может быть нежелательно. Как бы вы справились с этим?
Моше
12
Это, вероятно, очевидно для всех, но для меня код должен был быть self.isViewLoaded && self.view.window
JeffB6688
86
Будьте осторожны при обобщении этого решения на другие ситуации. Например, если вы используете UIPageViewController, представления для UIViewControllers, которые не являются текущей страницей, могут по-прежнему иметь свойство окна, отличное от nil, поскольку они отображаются вне экрана. В этом случае мне удалось создать собственное свойство isCurrentlyVisible, которое устанавливается в viewDidAppear и viewDidDisappear.
evanflash
4
@ Моше в этом случае, используйте topViewController.
ma11hew28
3
Обратите внимание, что этот ответ ничего не говорит о реальной видимости. Например, если приложение находится в фоновом режиме над оператором IF, он скажет «ДА», а представление в действительности не видно.
Марек Дж
89

Вот решение @ progrmr как UIViewControllerкатегория:

// 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
источник
47

Есть несколько проблем с вышеуказанными решениями. Если вы используете, например, a UISplitViewController, главное представление всегда будет возвращать true для

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

Вместо этого воспользуйтесь этим простым подходом, который, кажется, хорошо работает в большинстве, если не во всех случаях:

- (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
источник
1
Это все еще верно в xCode 7.1.1? Мастер в моем UISplitViewController возвращает NO для viewController.view.window. Возможно, я что-то делаю не так, но я уверен, что это так.
SAHM
44

Для тех из вас, кто ищет версию Swift 2.2, ответ:

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

и Свифт 3 :

if self.isViewLoaded && (self.view.window != nil) {
         // viewController is visible
}
Вениамин
источник
Не знаю почему, но я обнаружил, что выполнение self.view.window! = Nil приводит к тому, что оно никогда не работает, даже если self.isViewLoaded имеет значение true. После удаления работает нормально.
Мика Монтойя
это работало только для меня в viewDidAppear. Когда я добавил это в viewWillAppear self.view.window! = Nil всегда появлялся ноль
Ланс Самария
29

Для модального представления в полноэкранном или контекстном режиме «видимый» может означать, что он находится над стеком контроллера представления или просто видим, но покрыт другим контроллером представления.

Чтобы проверить, отличается ли контроллер вида «контроллер вида сверху» от «видимого», необходимо проверить стек контроллера представления контроллера навигации контроллера вида.

Я написал кусок кода, чтобы решить эту проблему:

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
источник
Хороший пост! FYI isViewLoadedявляется собственностью, начиная с Swift 3.0.
Yuchen Zhong
28

Вы хотите использовать UITabBarController«S selectedViewControllerсобственности. Все контроллеры представления, подключенные к контроллеру панели вкладок, имеют tabBarControllerнабор свойств, так что вы можете сделать это из любого кода контроллера представления:

if([[[self tabBarController] selectedViewController] isEqual:self]){
     //we're in the active controller
}else{
     //we are not
}
executor21
источник
2
Это не работает, если контроллер представления содержится внутри контроллера навигации и этот контроллер добавлен в контроллер панели вкладок. Вызов selectedViewController вернет контроллер навигации, а не текущий контроллер представления.
Антон Холмберг
2
@AntonHolmberg в этом случае, получите видимый контроллер вида, как это:((UINavigationController *)self.tabBarController.selectedViewController).visibleViewController
ma11hew28
Или даже используйте свойство self.tabBarController.selectedIndex, если мы зашли так далеко.
Владимир Шутюк
12

Я сделал быстрое расширение на основе ответа @ progrmr.

Это позволяет вам легко проверить, есть ли UIViewControllerна экране:

if someViewController.isOnScreen {
    // Do stuff here
}

Расширение:

//
//  UIViewControllerExtension.swift
//

import UIKit

extension UIViewController{
    var isOnScreen: Bool{
        return self.isViewLoaded() && view.window != nil
    }
}
Besi
источник
7

Для моих целей, в контексте контроллера представления контейнера, я обнаружил, что

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

работает хорошо.

Крис Принс
источник
3

Если вы используете UINavigationController, а также хотите обрабатывать модальные представления, я использую следующее:

#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
источник
2
Я нашел этот способ более надежным, чем принятый ответ, когда доступен контроллер навигации. Это можно сократить до: if ([self.navigationController.visibleViewController isKindOfClass: [self class]]) {
Даррен
3

Подход, который я использовал для модального представленного контроллера представления, состоял в том, чтобы проверить класс представленного контроллера. Если бы представленный контроллер представления был ViewController2тогда, я выполнил бы некоторый код.

UIViewController *vc = [self presentedViewController];

if ([vc isKindOfClass:[ViewController2 class]]) {
    NSLog(@"this is VC2");
}
разнос
источник
3

Я нашел эти функции в 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);

Возможно вышеупомянутые функции могут обнаружить ViewControllerпоявившееся или нет.

AechoLiu
источник
3

XCode 6.4, для iOS 8.4, ARC включен

Очевидно много способов сделать это. Тот, который работал для меня, следующий ...

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

Это может быть использовано в любом контроллере представления следующим образом,

[self.view.window isKeyWindow]

Если вы называете это свойство, -(void)viewDidLoadвы получаете 0, то если вы называете это после -(void)viewDidAppear:(BOOL)animatedтого, как вы получаете 1.

Надеюсь, это кому-нибудь поможет. Спасибо! Приветствия.

Serge-к
источник
3

Если вы используете контроллер навигации и просто хотите узнать, используете ли вы активный и самый верхний контроллер, используйте:

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

Этот ответ основан на комментарии @mattdipasquale .

Если у вас более сложный сценарий, посмотрите другие ответы выше.

phatmann
источник
это никогда не будет вызвано, если приложение идет в фоновом режиме, а затем на переднем плане. Я ищу решение, где я могу проверить, является ли контроллер представления видимым для пользователя или нет. Пользователь может фоновое приложение в течение нескольких дней, и когда он возвращается на передний план, я хотел бы обновить пользовательский интерфейс. Пожалуйста, дайте мне знать, если вы можете помочь.
bibscy
2

Вы можете проверить это по windowсвойству

if(viewController.view.window){

// view visible

}else{

// no visible

}
Саад Ур Рехман
источник
0

Мне нужно было это, чтобы проверить, является ли контроллер представления текущим просмотренным контроллером, я сделал это, проверив, есть ли представленный контроллер представления или протолкнул через навигатор, я отправляю это в случае, если кому-то нужно такое решение:

if presentedViewController != nil || navigationController?.topViewController != self {
      //Viewcontroller isn't viewed
}else{
     // Now your viewcontroller is being viewed 
}
Abdoelrhman
источник
0

Я использую это небольшое расширение в Swift 5 , что позволяет легко и просто проверять наличие любого объекта, который является членом UIView .

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

Тогда я просто использую его как простую проверку операторов ...

if myView.isVisible {
    // do something
}

Я надеюсь, что это помогает! :)

valbu17
источник