繁体   English   中英

如何实现检测点击事件的全局手势识别器?

[英]How to implement a global gesture recognizer which detects tap up event?

我尝试实现一个全局手势识别器,它能够全局检测点击事件。

以下是我的第一次尝试。

第一次尝试:全局点按手势识别器。 (不完美)

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能够捕获该事件。

在此处输入图片说明

但是,这仅限于点击事件。 我希望能够捕获点击事件。

有谁知道该怎么做? 我的期望是

  1. 全局手势不会阻止按钮、自定义视图的原始事件...
  2. 全局手势将能够在任何地方检测触摸事件。 即使事件落在按钮上,自定义视图......全局手势仍然能够检测到它们。

几年前我们做了一些事情来检测 iOS 上的状态栏触摸。 在 App Delegate 中,我们重写了一个函数,如下所示:


extension AppDelegate {

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        // do something here
    }
}

在该函数中,我们检查了触摸事件并将其位置与窗口的前 50 个像素进行了比较。

这是一个解决方案。

基本上,手指在屏幕上的实例由 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 等,即使在单个多点触摸序列中甚至同时发生多次触摸。

那里有一个布尔值,您可以切换以启用/禁用全局修饰检测功能。 哦,还有,确保你的 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
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM