web-dev-qa-db-ja.com

UIViewがユーザーに表示されるかどうかを判断しますか?

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

私のビューはsubviewとしてTab Bar Controllerに数回追加されます。

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

ただし、ユーザーに表示されていないビューを更新する必要はありません。

これは可能ですか?

ありがとう

66
jantimon

以下を確認できます。

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

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

73
mahboudz

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

UIViewが画面上にあるかどうかを判断するために、superview != nilwindow != nil。前者の場合、ビューにはスーパービューがあるが、スーパービューは画面上にない可能性があります。

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

もちろん、hiddenかどうか、またはalpha > 0

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

100
walkingbrad

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

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

潜在的な改善:

  • alphahiddenを尊重してください。
  • Falseの場合、ビューはそのスーパービューの境界を超える可能性があるため、clipsToBoundsを尊重してください。
18
John Gibb

私のために働いた解決策は、最初にビューにウィンドウがあるかどうかを確認し、次にスーパービューを反復して次のことを確認することでした:

  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
}
15
AlexGordon

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

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

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

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

3

ViewWillAppearで値「isVisible」をtrueに設定し、viewWillDisappearで値をfalseに設定します。 UITabBarControllerサブビューを知る最良の方法は、Navigation Controllerでも機能します。

2
BadPirate

これは、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
}
1
teradyl

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

view.hidden(Objective C)またはview.isHidden(Swift)は読み取り/書き込みプロパティです。簡単に読み書きできます

Swift 3.の場合

if(view.isHidden){
   print("Hidden")
}else{
   print("visible")
}
0

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

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
}
0
Andrey M.

これを試して:

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
}
0
Feng Chengjing