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


570

У меня есть приложение панели вкладок, с множеством просмотров. Есть ли способ узнать, UIViewControllerвидно ли конкретное в данный момент из 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 .


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

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;
}

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
        }
    }
}

Хороший пост! 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
}

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
    }
}

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
}

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появившееся или нет.


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.

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


3

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

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

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

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


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


0

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

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

0

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

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

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

if myView.isVisible {
    // do something
}

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.