Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to tell if UIViewController's view is visible

I have a tab bar application, with many views. Is there a way to know if a particular UIViewController is currently visible from within the UIViewController? (looking for a property)

like image 775
Rob Bonner Avatar asked May 05 '10 23:05

Rob Bonner


People also ask

What is the difference between ViewController and UIViewController?

UIViewController gives you an empty view, and you probably need some buttons, images, views etc. in it, to make the UI you need for your app. ViewController is there to make it easier to quickly create a simple app and to give people new to iOS programming an easier start in their project.

How do I get navigation controller ViewController?

The root view controller is simply the view controller that sits at the bottom of the navigation stack. You can access the navigation controller's array of view controllers through its viewControllers property. To access the root view controller, we ask for the first item of the array of view controllers.

How do I add a ViewController to a storyboard?

To create a new view controller, select File->New->File and select a Cocoa Touch Class. Choose whether to create it with Swift or Objective-C and inherit from UIViewController . Don't create it with a xib (a separate Interface Builder file), as you will most likely add it to an existing storyboard.


17 Answers

The view's window property is non-nil if a view is currently visible, so check the main view in the view controller:

Invoking the view method causes the view to load (if it is not loaded) which is unnecessary and may be undesirable. It would be better to check first to see if it is already loaded. I've added the call to isViewLoaded to avoid this problem.

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

Since iOS9 it has became easier:

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

Or if you have a UINavigationController managing the view controllers, you could check its visibleViewController property instead.

like image 176
progrmr Avatar answered Oct 06 '22 06:10

progrmr


There are a couple of issues with the above solutions. If you are using, for example, a UISplitViewController, the master view will always return true for

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

Instead, take this simple approach which seems to work well in most, if not all cases:

- (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;
}
like image 34
vincentjames501 Avatar answered Oct 06 '22 04:10

vincentjames501


Here's @progrmr's solution as a UIViewController category:

// 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
like image 22
ma11hew28 Avatar answered Oct 06 '22 05:10

ma11hew28


For those of you looking for a Swift 2.2 version of the answer:

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

and Swift 3:

if self.isViewLoaded && (self.view.window != nil) {
         // viewController is visible
}
like image 44
Benjamin Avatar answered Oct 06 '22 05:10

Benjamin


For over-full-screen or over-context modal presentation, "is visible" could mean it is on top of the view controller stack or just visible but covered by another view controller.

To check if the view controller "is the top view controller" is quite different from "is visible", you should check the view controller's navigation controller's view controller stack.

I wrote a piece of code to solve this 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
        }
    }
}
like image 45
WeZZard Avatar answered Oct 06 '22 06:10

WeZZard


You want to use the UITabBarController's selectedViewController property. All view controllers attached to a tab bar controller have a tabBarController property set, so you can, from within any of the view controllers' code:

if([[[self tabBarController] selectedViewController] isEqual:self]){
     //we're in the active controller
}else{
     //we are not
}
like image 35
executor21 Avatar answered Oct 06 '22 05:10

executor21


I made a swift extension based on @progrmr's answer.

It allows you to easily check if a UIViewController is on screen like so:

if someViewController.isOnScreen {
    // Do stuff here
}

The extension:

//
//  UIViewControllerExtension.swift
//

import UIKit

extension UIViewController{
    var isOnScreen: Bool{
        return self.isViewLoaded() && view.window != nil
    }
}
like image 41
Besi Avatar answered Oct 06 '22 06:10

Besi


For my purposes, in the context of a container view controller, I've found that

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

works well.

like image 33
Chris Prince Avatar answered Oct 06 '22 04:10

Chris Prince


XCode 6.4, for iOS 8.4, ARC enabled

Obviously lots of ways of doing it. The one that has worked for me is the following...

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

This can be used in any view controller in the following way,

[self.view.window isKeyWindow]

If you call this property in -(void)viewDidLoad you get 0, then if you call this after -(void)viewDidAppear:(BOOL)animated you get 1.

Hope this helps someone. Thanks! Cheers.

like image 38
serge-k Avatar answered Oct 06 '22 06:10

serge-k


I use this small extension in Swift 5, which keeps it simple and easy to check for any object that is member of UIView.

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

Then, I just use it as a simple if statement check...

if myView.isVisible {
    // do something
}

I hope it helps! :)

like image 27
valbu17 Avatar answered Oct 06 '22 05:10

valbu17


Good point that view is appeared if it's already in window hierarchy stack. thus we can extend our classes for this functionality.

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

extension UIView {
  var isAppeared: Bool { window != nil }
}
like image 22
dimpiax Avatar answered Oct 06 '22 06:10

dimpiax


if you're utilizing a UINavigationController and also want to handle modal views, the following is what i use:

#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
}
like image 28
MrTristan Avatar answered Oct 06 '22 04:10

MrTristan


I found those function in 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);

Maybe the above functions can detect the ViewController is appeared or not.

like image 23
AechoLiu Avatar answered Oct 06 '22 05:10

AechoLiu


The approach that I used for a modal presented view controller was to check the class of the presented controller. If the presented view controller was ViewController2 then I would execute some code.

UIViewController *vc = [self presentedViewController];

if ([vc isKindOfClass:[ViewController2 class]]) {
    NSLog(@"this is VC2");
}
like image 23
wigging Avatar answered Oct 06 '22 05:10

wigging


If you are using a navigation controller and just want to know if you are in the active and topmost controller, then use:

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

This answer is based on @mattdipasquale's comment.

If you have a more complicated scenario, see the other answers above.

like image 27
phatmann Avatar answered Oct 06 '22 04:10

phatmann


you can check it by window property

if(viewController.view.window){

// view visible

}else{

// no visible

}
like image 38
Saad Ur Rehman Avatar answered Oct 06 '22 04:10

Saad Ur Rehman


I needed this to check if the view controller is the current viewed controller, I did it via checking if there's any presented view controller or pushed through the navigator, I'm posting it in case anyone needed such a solution:

if presentedViewController != nil || navigationController?.topViewController != self {
      //Viewcontroller isn't viewed
}else{
     // Now your viewcontroller is being viewed 
}
like image 43
Abdoelrhman Avatar answered Oct 06 '22 06:10

Abdoelrhman