8
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Posted at

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

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でのインストールが可能ですので、是非試してみて頂けたら幸いです。
(ついでにスターも頂けると喜びます)

8
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?