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

Swe*_*per 1 uiview ios swift

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

根据此评论,我需要重写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)
    }
}
Run Code Online (Sandbox Code Playgroud)

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

我创建了一个MyView名为 的blueView,将其背景设置为蓝色,以便触摸区域为红色,非触摸区域为蓝色。

UIView我还在蓝色的一部分下添加了常规的绿色,如下所示:

在此输入图像描述

我为这两个视图启用了用户交互并添加了UITapGestureRecognisers,这样如果点击红色区域,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")
}
Run Code Online (Sandbox Code Playgroud)

当我运行应用程序并点击红色位时,仅point(inside:with:) called打印两次而没有其他内容。我预计blue view tapped所有其他打印语句point(inside:with:)也会运行。如果我点击蓝色位,相同的消息会再次打印两次。如果我点击被蓝色位覆盖的绿色位,Green view tapped则会在point(inside:with:) called打印两次后打印。

为什么blue view tapped不打印?一定是我执行point(inside:with:)错了,对吧?

编辑:

point(inside:with:)经过一番调试,我发现不返回true的原因是因为event.allTouches为空。这很奇怪,因为我 100% 确定我触摸了视图!

Don*_*Mag 5

由于您使用的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)

    }
}
Run Code Online (Sandbox Code Playgroud)