Determine if UIView is visible to the user?

前端 未结 12 1757
不知归路
不知归路 2020-12-07 15:21

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

相关标签:
12条回答
  • 2020-12-07 15:51

    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.

    0 讨论(0)
  • 2020-12-07 15:51

    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.

    0 讨论(0)
  • 2020-12-07 15:51

    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
    }
    
    0 讨论(0)
  • 2020-12-07 15:53

    try this:

    func isDisplayedInScreen() -> Bool
    {
     if (self == nil) {
         return false
      }
        let screenRect = UIScreen.main.bounds 
        // 
        let rect = self.convert(self.frame, from: nil)
        if (rect.isEmpty || rect.isNull) {
            return false
        }
        // 若view 隐藏
        if (self.isHidden) {
            return false
        }
    
        // 
        if (self.superview == nil) {
            return false
        }
        // 
        if (rect.size.equalTo(CGSize.zero)) {
            return  false
        }
        //
        let intersectionRect = rect.intersection(screenRect)
        if (intersectionRect.isEmpty || intersectionRect.isNull) {
            return false
        }
        return true
    }
    
    0 讨论(0)
  • 2020-12-07 15:54

    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.

    Swift 5.x:

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

    Older swift versions

    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.
    0 讨论(0)
  • 2020-12-07 15:58

    Tested solution.

    func isVisible(_ view: UIView) -> Bool {
        if view.isHidden || view.superview == nil {
            return false
        }
    
        if let rootViewController = UIApplication.shared.keyWindow?.rootViewController,
            let rootView = rootViewController.view {
    
            let viewFrame = view.convert(view.bounds, to: rootView)
    
            let topSafeArea: CGFloat
            let bottomSafeArea: CGFloat
    
            if #available(iOS 11.0, *) {
                topSafeArea = rootView.safeAreaInsets.top
                bottomSafeArea = rootView.safeAreaInsets.bottom
            } else {
                topSafeArea = rootViewController.topLayoutGuide.length
                bottomSafeArea = rootViewController.bottomLayoutGuide.length
            }
    
            return viewFrame.minX >= 0 &&
                   viewFrame.maxX <= rootView.bounds.width &&
                   viewFrame.minY >= topSafeArea &&
                   viewFrame.maxY <= rootView.bounds.height - bottomSafeArea
        }
    
        return false
    }
    
    0 讨论(0)
提交回复
热议问题