17273


Swiftでビューコントローラーの  
あるCGPointを含むsubviewsを取得する方法はありますか？

以下のようにfor文で全てのsubviewに対して、`containsPoint`をして、  
判定する方法しかないでしょうか？

[code]

    @IBAction func hundleTapGR(sender: UITapGestureRecognizer) {
    
            print("タップ")
            let point = sender.locationInView(self.view)
            let subviews = NSMutableArray()
    
            for v in self.view.subviews {
                if v.tag == 1 {
                    let view = v as! UIView
                    if view.layer.containsPoint(point) == true {
                        subviews.addObject(view)
                    }
                }
            }
            print(subviews)
        }
    
[/code]


CC BY-SA 3.0
2015-10-05T08:39:19.520
17268
2015-10-16T16:10:47.317
2015-10-16T16:10:47.317
5519
12297
question
swift
Swiftでビューコントローラーの あるCGPointを含むSubviewを取得する方法はありますか？


CGRectContainsPoint(_ rect: CGRect, _ point:
CGPoint)かUIView.pointInside(_:withEvent:)を使うと良いと思います。といっても地道に判定するのは変わりないですが。


CC BY-SA 3.0
2015-10-05T09:12:52.500
17270
2015-10-05T09:12:52.500
238
17268
answer


まず、Swift2.0の時代になっても、まだ`NS(Mutable)Array`にしがみつくことは、やめにしましょう。すなおにSwiftの`Array`型を使ってください。

[code]

    func hundleTapGR(recognizer: UITapGestureRecognizer) {
        print("タップ")
        let point = recognizer.locationInView(self.view)
        let views = self.view.subviews // Subviewsの配列を取得
        // Arrayのメソッド、filterを活用。pointがフレーム内にあるSubviewをフィルタリング。
        let viewsInPoint: [UIView] = views.filter({(subView: UIView) -> Bool in
            return subView.tag == 1 && CGRectContainsPoint(subView.frame, point)
        })
        結果を出力
        for view in viewsInPoint {
            print("x = \(view.center.x), y = \(view.center.y)")
        }
    }
    
[/code]

`Array`のメソッド、`map`、`reduce`、`filter`を使うと、`for`文で何行も書いていたコードが、1行で書けちゃいます。勉強する価値が、おおいにあると思いませんか？

* * *

「対象のViewが、座標変換（transform）していたらどないすんねん？」という問題が、別回答で提起されていますが、それは、わざわざ`convertPoint()`を使わなくても処理できます。`UIGestureRecognizer`のメソッド`locationInView()`の使いかたを工夫します。

[code]

    func tapped(recognizer: UITapGestureRecognizer) {
        let tappedViews = self.view.subviews.filter({(subview: UIView) -> Bool in
            return subview.bounds.contains(recognizer.locationInView(subview))
        })
        print("number = \(tappedViews.count)")
    }
    
[/code]

かえって、簡潔なコードになりましたね。


CC BY-SA 3.0
2015-10-05T11:00:06.333
17273
2015-10-06T06:17:36.970
2015-10-06T06:17:36.970
7362
7362
17268
answer


ダイレクトなAPIはありません。

`CALayer` の `containsPoint(_:)` メソッドは、レイヤーの `frame` ではなく `bounds`
がそのポイントを含むか否かなので、質問のコードはたぶん正常に動作しません。

Swift では `CGRect` が拡張されていて、`contains(point: CGPoint) -> Bool`
を持っていますので、一番手っ取り早いのは:

[code]

    let subviews = view.subviews.filter {
        $0.frame.contains(point)
    }
    
[/code]

なのですが、`frame`はそのviewに`transform`がかかっているときは[無意味な可能性がある](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/#//apple_ref/occ/instp/UIView/frame)ので:

> If the transform property is not the identity transform, the value of this
> property is undefined and therefore should be ignored.
[code]

    let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
    view.transform = CGAffineTransformMakeRotation(CGFloat(M_PI * 45 / 180))
    view.frame // -> {x -20.711 y -20.711 w 141.421 h 141.421}
    view.frame.contains(CGPoint(x: 1, y: 1)) // -> true
    
[/code]

このような場合に正確に調べたい場合は、座標系を変換して `bounds` と比較した方がよいです。

[code]

    let subviews = self.view.subviews.filter {
        $0.bounds.contains(view.convertPoint(point, toView: $0))
    }
    
[/code]

この場合 `$0.layer.containsPoint(view.convertPoint(point, toView: $0))`
と同じですが、`UIView` 使っているときには `.layer` は直接触らない方が良いと思っているので、僕は`bounds.contains`
を使います。


CC BY-SA 3.0
2015-10-05T13:36:52.890
17286
2015-10-05T13:36:52.890
3489
17268
answer