如何实现检测点击事件的全局手势识别器?
Posted
技术标签:
【中文标题】如何实现检测点击事件的全局手势识别器?【英文标题】:How to implement a global gesture recognizer which detects tap up event? 【发布时间】:2021-07-28 19:00:21 【问题描述】:我尝试实现一个全局手势识别器,它能够全局检测点击事件。
以下是我的第一次尝试。
第一次尝试:全局点击手势识别器。 (不完美)
import UIKit
extension UIWindow
static var key: UIWindow!
if #available(ios 13, *)
return UIApplication.shared.windows.first $0.isKeyWindow
else
return UIApplication.shared.keyWindow
class ViewController: UIViewController
// Lazy is required as self is not ready yet without lazy.
private lazy var globalGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(globalTapped))
private func installGlobalGestureRecognizer()
UIWindow.key.removeGestureRecognizer(globalGestureRecognizer)
UIWindow.key.addGestureRecognizer(globalGestureRecognizer)
@objc func globalTapped()
print("global tapped!")
override func viewDidLoad()
super.viewDidLoad()
@IBAction func handleTapForRedView(_ gesture: UITapGestureRecognizer)
print("red view tap")
@IBAction func yellowButtonTap(_ sender: Any)
print("yellow button tap")
@IBAction func installGlobalGestureButtonTap(_ sender: Any)
print("install global gesture")
installGlobalGestureRecognizer()
但是,这样的解决方案并不完美。当点击区域落在按钮等其他可触摸组件上时,globalGestureRecognizer
无法捕获事件。请参考以下视频。
正如您在视频中看到的,当修饰区域为黄色按钮或红色自定义视图时,“全局点击!”不会被打印出来。
我再试一次。
第二次尝试:只能检测到轻击(不完美)
import UIKit
extension UIWindow
static var key: UIWindow!
if #available(iOS 13, *)
return UIApplication.shared.windows.first $0.isKeyWindow
else
return UIApplication.shared.keyWindow
extension ViewController: UIGestureRecognizerDelegate
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool
print("global tapped down!")
return false
class ViewController: UIViewController
// Lazy is required as self is not ready yet without lazy.
private lazy var globalGestureRecognizer = UITapGestureRecognizer(target: self, action: nil)
private func installGlobalGestureRecognizer()
UIWindow.key.removeGestureRecognizer(globalGestureRecognizer)
UIWindow.key.addGestureRecognizer(globalGestureRecognizer)
globalGestureRecognizer.delegate = self
override func viewDidLoad()
super.viewDidLoad()
@IBAction func handleTapForRedView(_ gesture: UITapGestureRecognizer)
print("red view tap")
@IBAction func yellowButtonTap(_ sender: Any)
print("yellow button tap")
@IBAction func installGlobalGestureButtonTap(_ sender: Any)
print("install global gesture")
installGlobalGestureRecognizer()
正如您在视频中看到的,当点击区域落在按钮等其他可触摸组件上时,globalGestureRecognizer
能够捕获事件。
但是,这仅限于点击事件。我希望能够捕捉到点击事件。
有人知道怎么做吗?我的期望是
-
全局手势不会阻止按钮、自定义视图的原始事件...
全局手势将能够在任何地方检测到触摸事件。即使事件落在按钮、自定义视图...全局手势仍然能够检测到它们。
【问题讨论】:
得看touches began和touches ended? 【参考方案1】:几年前我们做了一些事情来检测 iOS 上的状态栏触摸。在 App Delegate 中,我们像这样覆盖了一个函数:
extension AppDelegate
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
super.touchesBegan(touches, with: event)
// do something here
在该函数中,我们检查了触摸事件并将其位置与窗口的前 50 个像素进行了比较。
【讨论】:
Hem...我们不希望覆盖 AppDelegate,因为我们希望能够启用/禁用此类全局修饰检测功能,而无需运行时成本。【参考方案2】:这里有一个解决方案。
基本上,屏幕上的手指实例由 UITouch 表示。多点触控序列从第一根手指在屏幕上落下时开始,在屏幕上没有更多手指时结束。一个 UITouch 对象本质上代表整个多点触控序列中的同一根手指。系统将所有这些 UITouch 对象打包在一个称为 UIEvent 的信封中。这被发送到我们的 UIWindow,它使用命中测试和其他东西将它发送到正确的视图。这是在它的 sendEvent(:) 方法中完成的。
如果我们继承 UIWindow,我们可以重写 sendEvent(:) 来拦截事件。我们所要做的就是查看该事件中的触摸并确定是否已结束,然后调用 super 正常发送事件。
class MyWindow: UIWindow
var touchUpDetectionEnabled = true
override func sendEvent(_ event: UIEvent)
super.sendEvent(event)
guard touchUpDetectionEnabled else return
let touchUps = event.allTouches!.filter $0.phase == .ended
for each in touchUps
print("?")
即使您正在滚动 UITableView 等,即使在单个多点触摸序列中甚至同时发生多个触摸,该代码也会在每次触摸发生时打印一个“?”。
其中有一个 bool,您可以切换以启用/禁用全局修饰检测功能。哦,请确保您的 AppDelegate 创建了我们的 UIWindow 子类的实例,并为其分配一个根视图控制器。
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate
var window: MyWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
// Override point for customization after application launch.
self.window = MyWindow()
self.window?.rootViewController = ViewController()
self.window?.makeKeyAndVisible()
return true
【讨论】:
以上是关于如何实现检测点击事件的全局手势识别器?的主要内容,如果未能解决你的问题,请参考以下文章