Determine if UIView is visible to the user?

前端 未结 12 1818
不知归路
不知归路 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 16:00

    I benchmarked both @Audrey M. and @John Gibb their solutions.
    And @Audrey M. his way performed better (times 10).
    So I used that one to make it observable.

    I made a RxSwift Observable, to get notified when the UIView became visible.
    This could be useful if you want to trigger a banner 'view' event

    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 {
            // Every second this will check `isVisibleToUser`
            return Observable.interval(.milliseconds(1000),
                                            scheduler: MainScheduler.instance)
            .flatMap { [base] _ in
                return Observable.just(base.isVisibleToUser)
            }.distinctUntilChanged()
        }
    }
    

    Use it as like this:

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

提交回复
热议问题