簡體   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