Viewが画面領域内にあるかどうかを判定する

SwiftでUIViewが画面領域(UIWindow)内にあるかどうかを判定する方法です。

色々と状況を考慮するとチェックすべきポイントがいくつかあります。

  1. 対象のUIView自身のisHiddenプロパティとalphaプロパティ
  2. 対象のUIViewから親にあたる階層全てのsuperviewisHiddenプロパティとalphaプロパティ
  3. 対象のUIViewと対象のUIViewの親にあたるUIWindowの交差領域とその面積比

更に正確に考慮を入れると対象のUIViewの上に覆いかぶさるViewも考える必要があるのですが、それについては出来ていません。

1と2のisHiddenalphaを親も含めて再帰的にチェックするのは下のような関数を考えました。

func isVisibleAllViewHierarchy(of view: UIView, alpha: CGFloat) -> Bool {
    var view: UIView? = view
    while view != nil, let v = view {
        let isHidden = v.isHidden
        let isTransparent = v.alpha < alpha
        if isHidden || isTransparent {
            return false;
        }
        view = v.superview
    }
    return true
}

引数に対象のUIViewと許容する透過率(alpha)を渡しtrueが返れば可視状態にあるという判定になります。

次に対象のUIViewUIWindowを渡し、UIWindowの領域内に対象のUIViewが入っているかどうかを判定する関数は以下のようになりました。

func isIntersectRect(target: UIView, parent: UIView, ratio: CGFloat) -> Bool {
    // convert(_:to:)でtoで渡したUIViewからの相対座標を取得
    let coordinates = target.convert(target.bounds, to: parent)
    // intersection(_:)で交差領域を取得
    let intersection = coordinates.intersection(parent.frame)
    // 交差領域の面積を計算
    let intersectionArea = floor(intersection.width * intersection.height)
    guard intersectionArea > 0 else {
        return false
    }
    // 対象UIViewの面積を計算
    let targetArea = floor(target.bounds.width * target.bounds.height)
    guard targetArea > 0 else {
        return false
    }
    // 引数の比率を計算して、対象UIViewが交差領域内かどうかを判定
    return intersectionArea >= (targetArea * ratio)
}

ポイントは上記のコメントに書いたように、CGRectのそれぞれの交差領域を割り出して判定するというところです。

ある機能を持ったViewコンポーネントが画面内に入ったら何かのアクションを行うといったデザインは結構あるんじゃないかなぁと思います。
ということで、それらを考慮したライブラリを作成しました。

https://github.com/darquro/Visibility

sample.gif

swiftのTargeted Extensionsを用いて、UIViewのプロパティとしてアクセスすることができます。

// start handling visibility
targetView.visibility
    .changed { state in
        switch state {
        case .visible:
            // visible process here
        case .invisible:
            // invisible process here
    }
}

// stop handling visibility
targetView.visibility.invalidate()

CocoaPods、Carthageでのインストールが可能ですので、是非試してみて頂けたら幸いです。
(ついでにスターも頂けると喜びます)

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.