Determine if UIView is visible to the user?

2019-01-21 12:09发布

问题:

is it possible to determine whether my UIView is visible to the user or not?

My View is added as subview several times into a Tab Bar Controller.

Each instance of this view has a NSTimer that updates the view.

However I don't want to update a view which is not visible to the user.

Is this possible?

Thanks

回答1:

You can check if:

  • it is hidden, by checking view.hidden
  • it is in the view hierarchy, by checking view.superview != nil
  • you can check the bounds of a view to see if it is on screen

The only other thing I can think of is if your view is buried behind others and can't be seen for that reason. You may have to go through all the views that come after to see if they obscure your view.



回答2:

For anyone else that ends up here:

To determine if a UIView is onscreen somewhere, rather than checking superview != nil, it is better to check if window != nil. In the former case, it is possible that the view has a superview but that the superview is not on screen:

if (view.window != nil) {
    // do stuff
}

Of course you should also check if it is hidden or if it has an alpha > 0.

Regarding not wanting your NSTimer running while the view is not visible, you should hide these views manually if possible and have the timer stop when the view is hidden. However, I'm not at all sure of what you're doing.



回答3:

This will determine if a view's frame is within the bounds of all of its superviews (up to the root view). One practical use case is determining if a child view is (at least partially) visible within a scrollview.

func isVisible(view: UIView) -> Bool {
    func isVisible(view: UIView, inView: UIView?) -> Bool {
        guard let inView = inView else { return true }
        let viewFrame = inView.convertRect(view.bounds, fromView: view)
        if CGRectIntersectsRect(viewFrame, inView.bounds) {
            return isVisible(view, inView: inView.superview)
        }
        return false
    }
    return isVisible(view, inView: view.superview)
}

Potential improvements:

  • Respect alpha and hidden.
  • Respect clipsToBounds, as a view may exceed the bounds of its superview if false.


回答4:

The solution that worked for me was to first check if the view has a window, then to iterate over superviews and check if:

  1. the view is not hidden.
  2. the view is within its superviews bounds.

Seems to work well so far.

Swift 3.0

public func isVisible(view: UIView) -> Bool {

  if view.window == nil {
    return false
  }

  var currentView: UIView = view
  while let superview = currentView.superview {

    if (superview.bounds).intersects(currentView.frame) == false {
      return false;
    }

    if currentView.isHidden {
      return false
    }

    currentView = superview
  }

  return true
}


回答5:

I you truly want to know if a view is visible to the user you would have to take into account the following:

  • Is the view's window not nil and equal to the top most window
  • Is the view, and all of its superviews alpha >= 0.01 (threshold value also used by UIKit to determine whether it should handle touches) and not hidden
  • Is the z-index (stacking value) of the view higher than other views in the same hierarchy.
  • Even if the z-index is lower, it can be visible if other views on top have a transparent background color, alpha 0 or are hidden.

Especially the transparent background color of views in front may pose a problem to check programmatically. The only way to be truly sure is to make a programmatic snapshot of the view to check and diff it within its frame with the snapshot of the entire screen. This won't work however for views that are not distinctive enough (e.g. fully white).

For inspiration see the method isViewVisible in the iOS Calabash-server project



回答6:

In viewWillAppear set a value "isVisible" to true, in viewWillDisappear set it to false. Best way to know for a UITabBarController subviews, also works for navigation controllers.



回答7:

This can help you figure out if your UIView is the top-most view. Can be helpful:

let visibleBool = view.superview?.subviews.last?.isEqual(view)
//have to check first whether it's nil (bc it's an optional) 
//as well as the true/false 
if let visibleBool = visibleBool where visibleBool { value
  //can be seen on top
} else {
  //maybe can be seen but not the topmost view
}


回答8:

In case you are using hidden property of view then :

view.hidden (objective C) or view.isHidden(swift) is read/write property. So you can easily read or write

For swift 3.0

if(view.isHidden){
   print("Hidden")
}else{
   print("visible")
}