UIView
自分がユーザーに表示されているかどうかを判断することはできますか?
マイビューは、にsubview
数回追加されTab Bar Controller
ます。
このビューの各インスタンスには、ビューNSTimer
を更新するがあります。
ただし、ユーザーに表示されないビューを更新したくありません。
これは可能ですか?
ありがとう
UIView
自分がユーザーに表示されているかどうかを判断することはできますか?
マイビューは、にsubview
数回追加されTab Bar Controller
ます。
このビューの各インスタンスには、ビューNSTimer
を更新するがあります。
ただし、ユーザーに表示されないビューを更新したくありません。
これは可能ですか?
ありがとう
ここで終わる他の人のために:
superview != nil
UIViewが画面のどこかに表示されているかどうかを確認するには、を確認するのではなく、を確認することをお勧めしますwindow != nil
。前者の場合、ビューにスーパービューがあるが、スーパービューが画面に表示されていない可能性があります。
if (view.window != nil) {
// do stuff
}
もちろん、そうであるhidden
かどうか、またはがあるかどうかも確認する必要がありalpha > 0
ます。
ビューが表示されていないときに実行したくないNSTimer
場合は、可能であればこれらのビューを手動で非表示にし、ビューが非表示になったときにタイマーを停止する必要があります。しかし、私はあなたが何をしているのか全くわかりません。
次のことを確認できます。
view.superview != nil
私が考えることができる他の唯一のことは、あなたの見解が他の人の後ろに埋もれていて、その理由で見ることができないかどうかです。後に続くすべてのビューを調べて、ビューが不明瞭になっていないかどうかを確認する必要がある場合があります。
これにより、ビューのフレームがすべてのスーパービューの範囲内にあるかどうかが決まります(ルートビューまで)。実用的なユースケースの1つは、子ビューが(少なくとも部分的に)スクロールビュー内に表示されるかどうかを判断することです。
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の場合、ビューがスーパービューの境界を超える可能性があるため、を尊重してください。私にとってうまくいった解決策は、最初にビューにウィンドウがあるかどうかを確認し、次にスーパービューを繰り返し処理して次のことを確認することでした。
これまでのところうまく機能しているようです。
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
}
@AudreyM.と@JohnGibbの両方のソリューションのベンチマークを行いました。
そして、@ Audrey M.の彼のやり方はより良く機能しました(10倍)。
だから私はそれを観察可能にするためにそれを使用しました。
UIViewが表示されたときに通知を受け取るために、RxSwiftObservableを作成しました。
これは、バナーの「表示」イベントをトリガーする場合に役立ちます。
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)
.map { [base] _ in
return 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)
}
ビューがユーザーに表示されるかどうかを本当に知りたいのですが、次の点を考慮する必要があります。
特に正面のビューの透明な背景色は、プログラムでチェックする際に問題を引き起こす可能性があります。本当に確実にする唯一の方法は、ビューのプログラムによるスナップショットを作成して、画面全体のスナップショットとフレーム内でビューを確認および差分することです。ただし、これは十分に特徴的でないビュー(完全に白など)では機能しません。
インスピレーションについては、iOSCalabashサーバープロジェクトのメソッドisViewVisibleを参照してください。
テスト済みのソリューション。
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
}
viewWillAppearでは値「isVisible」をtrueに設定し、viewWillDisappearでは値をfalseに設定します。UITabBarControllerサブビューを知るための最良の方法は、ナビゲーションコントローラーでも機能します。
もう1つの便利な方法は、didMoveToWindow()
例です。ViewControllerを押すと、前のViewControllerのビューがこのメソッドを呼び出します。self.window != nil
内部をチェックdidMoveToWindow()
すると、ビューが画面に表示されているか消えているかを知るのに役立ちます。
私が思いついた最も単純なSwift5ソリューションは、私の状況で機能しました(tableViewFooterに埋め込まれたボタンを探していました)。
John Gibbsソリューションも機能しましたが、私の原因では、すべての再帰は必要ありませんでした。
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
}
}
これは、UIViewが最上位のビューであるかどうかを判断するのに役立ちます。役立つ場合があります:
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
}
これを試して:
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
}
ビューの非表示プロパティを使用している場合:
view.hidden(Objective C)またはview.isHidden(swift)は読み取り/書き込みプロパティです。だからあなたは簡単に読み書きすることができます
swift3.0の場合
if(view.isHidden){
print("Hidden")
}else{
print("visible")
}