我应该如何实现point(inside:with :)?

时间:2018-10-29 08:51:03

标签: ios swift uiview

我正在尝试创建具有非矩形触摸区域的非矩形UIView。我已经知道如何用贝塞尔曲线绘制形状。

根据this comment,我需要覆盖point(inside:with:)以创建自定义形状的触摸区域。

所以我尝试了这个:

class MyView: UIView {
    override func draw(_ rect: CGRect) {
        let path = UIBezierPath()
        path.move(to: .zero)
        path.addLine(to: CGPoint(x: 0, y: bounds.maxY))
        path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY))
        path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.midY))
        path.addLine(to: CGPoint(x: bounds.midX, y: bounds.midY))
        path.addLine(to: CGPoint(x: bounds.midX, y: 0))
        path.addLine(to: CGPoint(x: 0, y: 0))
        UIColor.red.setFill()
        path.fill()
    }

    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        print("point(inside:with:) called")

        func isInRegion(_ point: CGPoint) -> Bool {
            return (0...bounds.midX).contains(point.x) || (bounds.midY...bounds.maxY).contains(point.y)
        }

        guard let touches = event?.touches(for: self) else { return false }
        guard !touches.isEmpty else { return false }

        print("passed all the guards")
        let first = touches.first!
        print("first touch: \(first.location(in: self))")

        return touches.map { $0.location(in: self) }.contains(where: isInRegion)
    }
}

draw方法绘制红色的L形。而且我尝试仅在L形内启用触摸。

我创建了一个名为MyView的{​​{1}},并将其背景设为蓝色,以便触摸区域为红色,非触摸区域为蓝色。

我还在蓝色的下面添加了常规的绿色blueView,如下所示:

enter image description here

我为这两个视图启用了用户交互,并添加了UIView,因此,如果点击红色区域,则会打印UITapGestureRecogniser。如果点击绿色视图,将打印blue view tapped

green view tapped

当我运行该应用程序并点击红色的位时,仅override func viewDidLoad() { super.viewDidLoad() blueView.isUserInteractionEnabled = true greenView.isUserInteractionEnabled = true blueView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(blueViewTapped))) greenView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(greenViewTapped))) } @objc func blueViewTapped() { print("Blue view tapped") } @objc func greenViewTapped() { print("Green view tapped") } 被打印两次,而没有其他内容。我希望point(inside:with:) calledblue view tapped中的所有其他打印语句也能运行。如果我点击蓝色位,则相同的消息会再次打印两次。如果我点击蓝色位所覆盖的绿色位,则point(inside:with:)会打印两次Green view tapped之后才打印。

为什么不打印point(inside:with:) called?我一定错误地实施了blue view tapped,对吧?

编辑:

经过调试后,我发现point(inside:with:)不返回true的原因是point(inside:with:)为空。这很奇怪,因为我100%确信触摸过该视图!

2 个答案:

答案 0 :(得分:1)

由于您使用的是UIBezierPath来绘制要点击的区域,因此可以通过保存对路径的引用并使用.contains(_:)来检测非矩形区域内的点:

class MyView: UIView {

    var path: UIBezierPath!

    override func draw(_ rect: CGRect) {
        path = UIBezierPath()
        path.move(to: .zero)
        path.addLine(to: CGPoint(x: 0, y: bounds.maxY))
        path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY))
        path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.midY))
        path.addLine(to: CGPoint(x: bounds.midX, y: bounds.midY))
        path.addLine(to: CGPoint(x: bounds.midX, y: 0))
        path.addLine(to: CGPoint(x: 0, y: 0))
        UIColor.red.setFill()
        path.fill()
    }

    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {

        print("path containsPoint?", path.contains(point))

        return path.contains(point)

    }
}

答案 1 :(得分:0)

point(inside:with:)使用

hitTest(_:with:)来确定是否应遍历子视图。

在您的情况下,如果用户点击蓝色区域,则应返回false。

您只需检查

if (point is inside blue+red rect) {
    return !(point is inside blue)
} else {
    return false
}

您无需测试触摸效果

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    print("point(inside:with:) called")

    func isInRegion(_ point: CGPoint) -> Bool {
        return (0...bounds.midX).contains(point.x) || (bounds.midY...bounds.maxY).contains(point.y)
    }
    let a = isInRegion(point)
    print(a)
    return a
}