如何判断UIViewController的视图是否可见

636

我有一个带有多个视图的选项卡应用程序。有没有一种方法可以从UIViewController中知道当前是否可见特定的UIViewController?(寻找属性)


18个回答

1209

如果视图当前可见,则视图的window property为非nil,因此请检查视图控制器中的主视图:

调用view方法会导致视图加载(如果尚未加载),这是不必要且可能不希望发生的。最好先检查是否已加载。我已添加了对isViewLoaded的调用以避免此问题。

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

iOS9之后,它变得更加容易了:

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

或者,如果您有一个管理视图控制器的UINavigationController,您可以检查其visibleViewController属性。


14
UINavigationController的visibleViewController属性有一个问题是当visibleViewController呈现模态视图控制器时,模态视图会变成visibleViewController,可能不符合预期。你会如何处理这种情况? - Moshe
13
这对每个人可能都很明显,但是对我来说,代码必须是self.isViewLoaded && self.view.window。 - JeffB6688
101
一般来说,在将此解决方案推广到其他情况时要小心。例如,如果您正在使用UIPageViewController,并且正在呈现非当前页面的UIViewController的视图,则这些视图可能仍具有非空的窗口属性,因为它们被渲染在屏幕外。在这种情况下,我成功地创建了自己的“isCurrentlyVisible”属性,该属性在viewDidAppear和viewDidDisappear中设置。 - evanflash
4
在这种情况下,使用topViewController - ma11hew28
4
请注意,这个回答并没有关于实际可见性的任何说明。例如,如果应用程序在后台运行,上述 IF 语句将会返回“是”,而实际上视图并不真正可见。 - Marek J.
显示剩余6条评论

91

这是@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

56

上述解决方案存在几个问题。例如,如果您正在使用 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中的主控制器对于viewController.view.window返回NO。我可能做错了什么,但我相当确定这是情况。 - SAHM

46

如果您正在寻找关于Swift 2.2的答案:

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

以及 Swift 3:

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

不知道为什么,但是我发现执行 self.view.window != nil 会导致它永远无法工作,即使 self.isViewLoaded 为 true。一旦移除,它就可以正常工作。 - Micah Montoya
1
这个只有在viewDidAppear方法中才适用。当我把它加到viewWillAppear方法中时,self.view.window != nil总是为nil。 - Lance Samaria

35

对于超出全屏或上下文模态呈现,"可见"可能意味着它在视图控制器堆栈的顶部或仅可见但被另一个视图控制器覆盖。

检查视图控制器是否为"顶部视图控制器"与"可见"相差很大,您应该检查视图控制器的导航控制器的视图控制器堆栈。

我编写了一段代码来解决这个问题:

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

28

您要使用UITabBarControllerselectedViewController属性。所有连接到选项卡栏控制器的视图控制器都有一个设置了tabBarController属性,因此在任何视图控制器的代码中,您可以:

if([[[self tabBarController] selectedViewController] isEqual:self]){
     //we're in the active controller
}else{
     //we are not
}

2
如果视图控制器包含在导航控制器中,并且该控制器已添加到选项卡控制器中,则此方法无法正常工作。调用selectedViewController将返回导航控制器而不是当前的视图控制器。 - Anton Holmberg
2
在这种情况下,可以像这样获取可见的视图控制器:((UINavigationController *)self.tabBarController.selectedViewController).visibleViewController - ma11hew28
甚至可以使用“self.tabBarController.selectedIndex”属性,如果我们已经到了这一步。 - Vladimir Shutyuk

14

我基于@progrmr的答案创建了一个Swift扩展。

它使你可以轻松地检查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);
}

运行良好。


6

我在 Swift 5 中使用了这个小扩展,它使得检查任何属于 UIView 的对象变得简单易懂。

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

然后,我把它作为一个简单的if语句进行检查...
if myView.isVisible {
    // do something
}

我希望能对您有所帮助!:)

5

如果显示视图已经存在于窗口层次结构栈中,则会出现良好的视图。因此我们可以扩展我们的类以实现此功能。

extension UIViewController {
  var isViewAppeared: Bool { viewIfLoaded?.isAppeared == true }
}

extension UIView {
  var isAppeared: Bool { window != nil }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接