jan*_*mon 69 iphone visibility uitabbarcontroller nstimer uiview
是否可以确定我UIView的用户是否可见?
我的视图被subview多次添加到一个Tab Bar Controller.
此视图的每个实例都有一个NSTimer更新视图的实例.
但是,我不想更新用户不可见的视图.
这可能吗?
谢谢
wal*_*rad 107
对于其他任何人来说:
要确定UIView是否在某个屏幕上,而不是检查superview != nil,最好检查是否window != nil.在前一种情况下,视图可能具有超级视图但超级视图不在屏幕上:
if (view.window != nil) {
    // do stuff
}
当然你也应该检查它是否有,hidden或者是否有alpha > 0.
关于NSTimer在视图不可见时不想运行的情况,如果可能,应手动隐藏这些视图,并在隐藏视图时停止计时器.但是,我完全不确定你在做什么.
mah*_*udz 74
您可以检查是否:
view.superview != nil我能想到的唯一另一件事就是如果你的观点被埋没在其他人的背后,并且由于这个原因而无法看到.您可能需要查看之后的所有视图,看看它们是否模糊了您的观点.
Joh*_*ibb 19
这将确定视图的帧是否在其所有超级视图的范围内(直到根视图).一个实际用例是确定子视图是否(至少部分地)在滚动视图中可见.
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,如果视图可能超出其超级视图的界限,如果是错误的.Ale*_*don 15
对我有用的解决方案是首先检查视图是否有窗口,然后迭代超视图并检查是否:
到目前为止似乎运作良好.
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
}
经过测试的解决方案。
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
}
我对@Audrey M. 和@John Gibb 他们的解决方案进行了基准测试。
而@Audrey M. 他的方式表现得更好(10 倍)。
所以我用那个来让它变得可观察。
我创建了一个 RxSwift Observable,以便在 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)
}
| 归档时间: | 
 | 
| 查看次数: | 67864 次 | 
| 最近记录: |