87

UIView自分がユーザーに表示されているかどうかを判断することはできますか?

マイビューは、にsubview数回追加されTab Bar Controllerます。

このビューの各インスタンスには、ビューNSTimerを更新するがあります。

ただし、ユーザーに表示されないビューを更新したくありません。

これは可能ですか?

ありがとう

4

13 に答える 13

131

ここで終わる他の人のために:

superview != nilUIViewが画面のどこかに表示されているかどうかを確認するには、を確認するのではなく、を確認することをお勧めしますwindow != nil。前者の場合、ビューにスーパービューがあるが、スーパービューが画面に表示されていない可能性があります。

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

もちろん、そうであるhiddenかどうか、またはがあるかどうかも確認する必要がありalpha > 0ます。

ビューが表示されていないときに実行したくないNSTimer場合は、可能であればこれらのビューを手動で非表示にし、ビューが非表示になったときにタイマーを停止する必要があります。しかし、私はあなたが何をしているのか全くわかりません。

于 2013-11-12T21:26:42.603 に答える
82

次のことを確認できます。

  • view.hiddenをチェックすることにより、非表示になります
  • チェックすることにより、ビュー階層にありますview.superview != nil
  • ビューの境界をチェックして、画面に表示されているかどうかを確認できます

私が考えることができる他の唯一のことは、あなたの見解が他の人の後ろに埋もれていて、その理由で見ることができないかどうかです。後に続くすべてのビューを調べて、ビューが不明瞭になっていないかどうかを確認する必要がある場合があります。

于 2009-10-11T07:19:07.120 に答える
36

これにより、ビューのフレームがすべてのスーパービューの範囲内にあるかどうかが決まります(ルートビューまで)。実用的なユースケースの1つは、子ビューが(少なくとも部分的に)スクロールビュー内に表示されるかどうかを判断することです。

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
  • clipsToBoundsfalseの場合、ビューがスーパービューの境界を超える可能性があるため、を尊重してください。
于 2016-01-06T20:15:21.673 に答える
20

私にとってうまくいった解決策は、最初にビューにウィンドウがあるかどうかを確認し、次にスーパービューを繰り返し処理して次のことを確認することでした。

  1. ビューは非表示になりません。
  2. ビューはそのスーパービューの範囲内にあります。

これまでのところうまく機能しているようです。

Swift 3.0

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
}
于 2017-07-14T13:06:23.013 に答える
7

@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)
}
于 2019-10-10T07:57:00.550 に答える
3

ビューがユーザーに表示されるかどうかを本当に知りたいのですが、次の点を考慮する必要があります。

  • ビューのウィンドウはnilではなく、一番上のウィンドウと同じですか
  • ビューであり、そのすべてのスーパービューはalpha> = 0.01(タッチを処理するかどうかを決定するためにUIKitでも使用されるしきい値)であり、非表示ではありません
  • ビューのz-index(スタッキング値)は、同じ階層内の他のビューよりも高くなっていますか。
  • z-indexが低くても、上部の他のビューの背景色が透明(アルファ0)であるか、非表示になっている場合に表示される可能性があります。

特に正面のビューの透明な背景色は、プログラムでチェックする際に問題を引き起こす可能性があります。本当に確実にする唯一の方法は、ビューのプログラムによるスナップショットを作成して、画面全体のスナップショットとフレーム内でビューを確認および差分することです。ただし、これは十分に特徴的でないビュー(完全に白など)では機能しません。

インスピレーションについては、iOSCalabashサーバープロジェクトのメソッドisViewVisibleを参照してください。

于 2016-11-07T12:04:13.883 に答える
3

テスト済みのソリューション。

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
}
于 2019-08-06T11:30:09.523 に答える
2

viewWillAppearでは値「isVisible」をtrueに設定し、viewWillDisappearでは値をfalseに設定します。UITabBarControllerサブビューを知るための最良の方法は、ナビゲーションコントローラーでも機能します。

于 2016-01-26T18:34:15.837 に答える
2

もう1つの便利な方法は、didMoveToWindow() 例です。ViewControllerを押すと、前のViewControllerのビューがこのメソッドを呼び出します。self.window != nil内部をチェックdidMoveToWindow()すると、ビューが画面に表示されているか消えているかを知るのに役立ちます。

于 2020-05-16T19:11:49.277 に答える
2

私が思いついた最も単純な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
        }
    }
于 2021-02-10T15:22:43.357 に答える
1

これは、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
}
于 2016-04-26T20:53:39.073 に答える
0

これを試して:

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
}
于 2019-03-13T10:35:46.153 に答える
-4

ビューの非表示プロパティを使用している場合:

view.hidden(Objective C)またはview.isHidden(swift)は読み取り/書き込みプロパティです。だからあなたは簡単に読み書きすることができます

swift3.0の場合

if(view.isHidden){
   print("Hidden")
}else{
   print("visible")
}
于 2017-05-08T12:17:11.857 に答える