我正在尝试创建具有非矩形触摸区域的非矩形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
,如下所示:
我为这两个视图启用了用户交互,并添加了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:) called
和blue view tapped
中的所有其他打印语句也能运行。如果我点击蓝色位,则相同的消息会再次打印两次。如果我点击蓝色位所覆盖的绿色位,则point(inside:with:)
会打印两次Green view tapped
之后才打印。
为什么不打印point(inside:with:) called
?我一定错误地实施了blue view tapped
,对吧?
编辑:
经过调试后,我发现point(inside:with:)
不返回true的原因是point(inside:with:)
为空。这很奇怪,因为我100%确信触摸过该视图!
答案 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)
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
}