我正在尝试创建一个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
我还在蓝色的一部分下添加了常规的绿色,如下所示:
我为这两个视图启用了用户交互并添加了UITapGestureRecogniser
s,这样如果点击红色区域,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% 确定我触摸了视图!
由于您使用的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)