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


Answers:


1096

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

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

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

从iOS9开始,它变得更加容易:

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

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


11
UINavigationController的visibleViewControllee属性的一个问题是您的visibleViewController呈现模式视图控制器的情况。在这种情况下,模式视图将成为visibleViewController,这可能是不希望的。您将如何处理?
Moshe

12
这可能对每个人都是显而易见的,但是对我来说,代码必须是self.isViewLoaded &&
self.view.window

85
在将此解决方案推广到其他情况时要小心。例如,如果您使用的是UIPageViewController,则不是当前页面的UIViewController的视图可能仍具有非nil window属性,因为它们是在屏幕外呈现的。在这种情况下,我已经成功制作了自己的“ isCurrentlyVisible”属性,该属性已在viewDidAppear和viewDidDisappear中设置。
evanflash 2014年

4
在这种情况下,请使用@Moshe topViewController
ma11hew28 2014年

3
请注意,此答案没有说明真实可见性。例如,如果应用程序在上面的背景中,则IF语句在视图不真正可见时会说是。
Marek J.

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返回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

44

对于那些正在寻找答案的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.isViewLoaded为true,执行self.view.window!= nil也会使它永远无法工作。一旦删除,它就可以正常工作。
米卡蒙托亚

这仅在viewDidAppear中对我有用。当我将其添加到viewWillAppear self.view.window!=总是nil总是nil
Lance Samaria

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

好贴!isViewLoaded自从Swift 3.0起,FYI 就成为了一家酒店。
Yuchen Zhong

28

您要使用UITabBarControllerselectedViewController属性。连接到选项卡栏控制器的所有视图控制器都具有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 2014年

如果我们走了这么远,甚至可以使用'self.tabBarController.selectedIndex'属性。
弗拉基米尔·舒秋克

12

我根据@progrmr的答案进行了快速扩展。

它使您可以轻松检查a 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]]){
Darren

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的评论。

如果您有更复杂的情况,请参见上面的其他答案。


如果应用程序先进入后台又进入前台,则永远不会调用此方法。我正在寻找一种解决方案,可以检查视图控制器对用户是否可见。用户可以将应用程序后台运行几天,当它回到前台时,我想更新UI。如果您可以提供帮助,请告诉我。
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语句检查...

if myView.isVisible {
    // do something
}

希望对您有所帮助!:)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.