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