代码之家  ›  专栏  ›  技术社区  ›  jantimon

确定用户是否可以看到UIView?

  •  62
  • jantimon  · 技术社区  · 16 年前

    UIView 是否对用户可见?

    我的观点被添加为 subview Tab Bar Controller .

    此视图的每个实例都有一个 NSTimer

    这可能吗?

    谢谢

    8 回复  |  直到 13 年前
        1
  •  82
  •   Albert Renshaw    10 年前

    您可以检查:

    • 通过检查,它位于视图层次结构中 view.superview != nil
    • 您可以检查视图的边界以查看它是否在屏幕上

        2
  •  127
  •   walkingbrad    12 年前

    对于在这里结束的其他人:

    确定UIView是否在屏幕上的某个位置,而不是检查 superview != nil ,最好检查一下 window != nil . 在前一种情况下,视图可能具有superview,但superview不在屏幕上:

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

    当然,你也应该检查它是否正确 hidden alpha > 0 .

    关于不想要你的 NSTimer 在视图不可见时运行,如果可能,应手动隐藏这些视图,并在视图隐藏时停止计时器。然而,我一点也不确定你在做什么。

        3
  •  33
  •   gabriel_vincent    6 年前

    这将确定视图的框架是否在其所有超级视图(直到根视图)的边界内。一个实际用例是确定子视图在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)
    }
    

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

    可能的改进:

    • alpha hidden .
    • clipsToBounds ,因为如果为false,视图可能会超出其superview的边界。
        4
  •  20
  •   Alechko    9 年前

    对我有效的解决方案是首先检查视图是否有窗口,然后在超级视图上迭代并检查是否:

    到目前为止似乎效果不错。

    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
  •  5
  •   basvk    6 年前

    我对这两个都进行了基准测试@Audrey M。还有约翰·吉布,他们的解决方案。
    和@Audrey M。他的方式表现得更好(10倍)。
    所以我用了这个来让它可见。

    我制作了一个RxSwift可观察,以便在UIView可见时得到通知。
    如果要触发横幅“查看”事件,这可能很有用

    import Foundation
    import UIKit
    import RxSwift
    
    extension UIView {
        var isVisibleToUser: Bool {
    
            if isHidden || alpha == 0 || superview == nil {
                return false
            }
    
            guard let rootViewController = UIApplication.shared.keyWindow?.rootViewController else {
                return false
            }
    
            let viewFrame = convert(bounds, to: rootViewController.view)
    
            let topSafeArea: CGFloat
            let bottomSafeArea: CGFloat
    
            if #available(iOS 11.0, *) {
                topSafeArea = rootViewController.view.safeAreaInsets.top
                bottomSafeArea = rootViewController.view.safeAreaInsets.bottom
            } else {
                topSafeArea = rootViewController.topLayoutGuide.length
                bottomSafeArea = rootViewController.bottomLayoutGuide.length
            }
    
            return viewFrame.minX >= 0 &&
                viewFrame.maxX <= rootViewController.view.bounds.width &&
                viewFrame.minY >= topSafeArea &&
                viewFrame.maxY <= rootViewController.view.bounds.height - bottomSafeArea
    
        }
    }
    
    extension Reactive where Base: UIView {
        var isVisibleToUser: Observable<Bool> {
            // Every second this will check `isVisibleToUser`
            return Observable<Int>.interval(.milliseconds(1000),
                                            scheduler: MainScheduler.instance)
            .flatMap { [base] _ in
                return Observable.just(base.isVisibleToUser)
            }.distinctUntilChanged()
        }
    }
    

    按如下方式使用:

    import RxSwift
    import UIKit
    import Foundation
    
    private let disposeBag = DisposeBag()
    
    private func _checkBannerVisibility() {
    
        bannerView.rx.isVisibleToUser
            .filter { $0 }
            .take(1) // Only trigger it once
            .subscribe(onNext: { [weak self] _ in
                // ... Do something
            }).disposed(by: disposeBag)
    }
    
        6
  •  3
  •   Werner Altewischer    9 年前

    我想知道用户是否可以看到视图,您必须考虑以下几点:

    • 视图的窗口是否不为零并等于最顶部的窗口
    • 是视图及其所有超级视图alpha>=0.01(UIKit也使用阈值来确定是否应处理触摸)且不隐藏
    • 视图的z索引(堆叠值)高于同一层次中的其他视图。
    • 即使z索引较低,如果顶部的其他视图具有透明背景色(alpha 0)或隐藏,也可以看到z索引。

    尤其是前面视图的透明背景色可能会对编程检查造成问题。唯一能真正确定的方法是制作视图的编程快照,以便在其框架内检查并将其与整个屏幕的快照区分开来。但是,对于不够独特的视图(例如全白色视图),这将不起作用。

    iOS Calabash-server project

        7
  •  3
  •   Andrey M.    7 年前

    测试溶液。

    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
    }
    
        8
  •  2
  •   BadPirate    10 年前

    在ViewWillEmerge中,将值“isVisible”设置为true;在ViewWillEmerge中,将其设置为false。了解UITabBarController子视图的最佳方法也适用于导航控制器。

        9
  •  1
  •   teradyl    10 年前

    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
    }
    
        10
  •  1
  •   George Sabanov    6 年前

    另一个有用的方法是 didMoveToWindow() 示例:推送视图控制器时,上一个视图控制器的视图将调用此方法。检查 self.window != nil 里面 didMoveToWindow() 有助于了解您的视图在屏幕上是显示还是消失。

        11
  •  1
  •   RyanG    5 年前

        func scrollViewDidScroll(_ scrollView: UIScrollView) {
            let viewFrame = scrollView.convert(targetView.bounds, from: targetView)
            if viewFrame.intersects(scrollView.bounds) {
                // targetView is visible 
            }
            else {
                // targetView is not visible
            }
        }
    
        12
  •  0
  •   Mikev    7 年前

    试试这个:

    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
    }
    
        13
  •  -3
  •   Deepak Chaudhary    9 年前

    如果您使用的是视图的隐藏属性,则:

    适用于swift 3.0

    if(view.isHidden){
       print("Hidden")
    }else{
       print("visible")
    }
    
    推荐文章