我应该如何实现 point(inside:with:)? [英] How am I supposed to implement point(inside:with:)?

查看:27
本文介绍了我应该如何实现 point(inside:with:)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

根据

我为这两个视图启用了用户交互并添加了 UITapGestureRecognisers,以便如果点击红色区域,将打印蓝色视图点击.如果点击绿色视图,将打印绿色视图点击.

override func viewDidLoad() {super.viewDidLoad()blueView.isUserInteractionEnabled = truegreenView.isUserInteractionEnabled = trueblueView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(blueViewTapped)))greenView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(greenViewTapped)))}@objc func blueViewTapped() {print("蓝色视图已点击")}@objc func greenViewTapped() {打印(绿色视图已点击")}

当我运行应用程序并点击红色位时,只有 point(inside:with:) called 被打印两次,没有其他内容.我希望 blue view tappedpoint(inside:with:) 中的所有其他打印语句也能运行.如果我点击蓝色位,相同的消息将再次打印两次.如果我点击被蓝色位覆盖的绿色位,Green view tappedpoint(inside:with:) called 被打印两次之后打印.

为什么没有打印蓝色视图?我一定是错误地实现了 point(inside:with:),对吧?

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

解决方案

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

class MyView: UIView {变量路径:UIBezierPath!覆盖 func draw(_ rect: CGRect) {路径 = UIBezierPath()路径.移动(到:.零)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()路径填充()}覆盖功能点(内点:CGPoint,事件:UIEvent?)->布尔{print("path containsPoint?", path.contains(point))返回路径.包含(点)}}

I am trying to create a non-rectangular shaped UIView, with a non-rectangular shaped touch area. I already know how to draw my shape with bezier paths.

According to this comment, I need to override point(inside:with:) to create a custom shaped touch area.

So I tried this:

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)
    }
}

The draw method draws a red L shape. And I tried enable touch only inside the L shape.

I created a MyView called blueView, made its background blue so that the touch area is red and the non-touch area is blue.

I also added a regular green UIView under part of the blue, like this:

I enabled user interactions for both of these views and added UITapGestureRecognisers, so that if the red area is tapped, blue view tapped will be printed. If the green view is tapped, green view tapped will be printed.

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")
}

When I run the app and tap the red bit, only point(inside:with:) called is printed twice and nothing else. I expected blue view tapped and all the other print statements in point(inside:with:) to be run as well. If I tap the blue bit, the same message is printed twice again. If I tap the green bit that is covered by the blue bit, Green view tapped is printed after point(inside:with:) called is printed twice.

Why is blue view tapped not printed? I must have implemented point(inside:with:) wrongly, right?

EDIT:

After some debugging, I found out that the reason why point(inside:with:) is not returning true is because event.allTouches is empty. This is very strange because I am 100% sure that I touched the view!

解决方案

Since you are using UIBezierPath to draw the area you want to tap, you can detect points inside your non-rectangular area by saving a reference to your path and using .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)

    }
}

这篇关于我应该如何实现 point(inside:with:)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆