我想实现
UIView
子类,它将捕获所有触摸事件(尤其是 TouchMoved)并传递它们。可以吗?
class PassthroughView: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let view = super.hitTest(point, with: event)
return view == self ? nil : view
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("touchesBegan")
super.touchesBegan(touches, with: event)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
print("touchesMoved") //not called
super.touchesMoved(touches, with: event)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
print("touchesEnded")
super.touchesEnded(touches, with: event)
}
}
你真的做不到这一点。如果您的视图接收到触摸,它无法将其传递给如果您的视图不存在则接收到的视图。为此,您基本上必须重新实现整个事件路由。如果不访问 Apple 内部 API,这将非常困难,甚至可能是不可能的。但即使你做对了,它也可能会随着每次 iOS 更新而崩溃。
但是有一种不同的方法来获取所有触摸事件,同时仍然将它们传递到适当的视图。为此,您创建
UIApplication
的子类并重写 sendEvent(_:)
方法。每个事件都会调用此方法并将其分派到适当的视图。从那里您可以先进行特殊处理,然后调用 super.sendEvent()
将其像往常一样发送到您的窗口。
当然,您不会收到不同的触摸开始/移动/结束调用,但信息存在于您的事件对象中。首先,您需要检查
event.type
属性以查看是否确实收到了触摸事件。如果是这样,您可以从 event.allTouches
属性获取所有触摸。对于每次触摸,您都可以获得 phase
,它描述了此触摸是否开始、移动、结束等。
当然子类化还不够,你还必须告诉系统使用你的新子类。为此,您将其注释为
@objc(MyApplication)
,其中 MyApplication
是类的名称。这可以确保 Objective-C 运行时可以通过您指定的名称找到该类。然后编辑 Info.plist 文件并将此名称设置为“Principal class”键。 (如果您不使用 Xcode 编辑器,该键也称为 NSPrincipalClass
)。
我使用这个类:
class TouchlessView: UIView {
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let buttons = findViews(subclassOf: UIButton.self)
for button in buttons where button.frame.contains(point) {
return true
}
return false
}
}
我在视图中添加了平移手势识别器。