为啥当点击区域落在按钮上时 UIGestureRecognizerDelegate 的 shouldRecognizeSimultaneouslyWith 没有被执行?

Posted

技术标签:

【中文标题】为啥当点击区域落在按钮上时 UIGestureRecognizerDelegate 的 shouldRecognizeSimultaneouslyWith 没有被执行?【英文标题】:Why UIGestureRecognizerDelegate 's shouldRecognizeSimultaneouslyWith is not executed, when the tap region fall on button?为什么当点击区域落在按钮上时 UIGestureRecognizerDelegate 的 shouldRecognizeSimultaneouslyWith 没有被执行? 【发布时间】:2021-07-30 10:20:16 【问题描述】:

我想要

    检测屏幕任意位置的全局修饰事件 不会影响按钮、自定义视图等组件。下面的按钮,自定义视图仍然能够接收点击事件。

我做的是

    UIApplication.shared.keyWindow 中安装UITapGestureRecognizerUIGestureRecognizerDelegate 中,为shouldRecognizeSimultaneouslyWith 返回true,这样***keyWindow 就不会阻止按钮、自定义视图接收点击事件。

这是我的代码

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,
           shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer)
            -> Bool 
        print(">>> shouldRecognizeSimultaneouslyWith returns true")
        return true
    


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)
        
        globalGestureRecognizer.delegate = self
    

    @objc func globalTapped() 
        print(">>> global tapped")
    
    
    override func viewDidLoad() 
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    

    @IBAction func buttonClicked(_ sender: Any) 
        print("yellow button tap\n")
    
    
    @IBAction func handleTap(_ gesture: UITapGestureRecognizer) 
        print("red view tap\n")
    
    
    @IBAction func installButtonClicked(_ sender: Any) 
        print("install global gesture")
        installGlobalGestureRecognizer()
    

这是点击红色自定义视图时发生的情况

点击红色自定义视图时(按预期工作)

install global gesture
>>> shouldRecognizeSimultaneouslyWith returns true
>>> shouldRecognizeSimultaneouslyWith returns true
>>> global tapped
red view tap

点击黄色按钮时(全局手势不起作用)

install global gesture
yellow button tap

这就是我为自定义红色视图和黄色按钮安装点击事件处理程序的方式。

有谁知道,为什么在点击按钮时不调用shouldRecognizeSimultaneouslyWith?我希望在点击黄色按钮时

shouldRecognizeSimultaneouslyWith 执行并返回 true 黄色按钮点击事件处理程序已执行 已执行全局手势点击事件处理程序

谢谢。

【问题讨论】:

部分问题是UIControl 上的.touchUpInside 不是Tap 手势。您可能需要另一种方法。这是一篇可能值得一读的(较旧的)文章:dzone.com/articles/… ...您可能想通过Combine查看接收和处理事件 我在your previous question 中发布了一个适用于所有情况的全局修饰的简单解决方案。它不符合您的要求吗?还是你没看到 【参考方案1】:

正如@DonMag 正确所说,发生这种情况是因为UIGestureRecognizers 没有处理按钮触摸

UIControl 直接从UIWindow 获得接触,而它又从UIApplication 获得接触。您可以将其中任何一个子类化以处理触摸。

    要使用UIWindow 子类,您需要在SceneDelegate 中创建其实例,然后手动重新初始化情节提要,如下所示:
class CustomWindow: UIWindow 
    override func sendEvent(_ event: UIEvent) 
        super.sendEvent(event)
        print(#function, event)
    


class SceneDelegate: UIResponder, UIWindowSceneDelegate 
    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) 
        guard let scene = (scene as? UIWindowScene) else  return 
        window = CustomWindow(windowScene: scene)
        window?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()
    

    我更喜欢使用UIApplication 子类。它需要更多的更改,但不需要重新初始化逻辑。另外,如果你在 iPad 上使用了很多场景,你也不需要考虑它:
子类 UIApplication
class CustomUIApplication: UIApplication 
    override func sendEvent(_ event: UIEvent) 
        super.sendEvent(event)
        print(#function, event)
    

AppDelegate 中删除@main 注释 创建main.swift,内容如下:
UIApplicationMain(
    CommandLine.argc,
    CommandLine.unsafeArgv,

    NSStringFromClass(CustomUIApplication.self),
    NSStringFromClass(AppDelegate.self)
)

【讨论】:

【参考方案2】:

将 UIView 作为背景视图,然后将 UIButton 作为背景视图层上方的不同层(重要:应确认 UIButton 未放置在新的背景视图内)并将手势识别器提供给背景视图.

【讨论】:

【参考方案3】:

原因是在viewDidLoad() 期间,您的ViewController 的某些属性尚未定义,并且还没有准备好呈现另一个ViewController

为了更好地理解enter link description here

【讨论】:

以上是关于为啥当点击区域落在按钮上时 UIGestureRecognizerDelegate 的 shouldRecognizeSimultaneouslyWith 没有被执行?的主要内容,如果未能解决你的问题,请参考以下文章

鼠标悬停事件的JS问题以显示额外的按钮

当AD域与DNS不再一台服务器上时配置

为啥UIButton高亮区域那么小?

为啥我的 Jquery 可拖动对象放置在三个可拖放对象之一上时会跳转?

为啥当我更改它的 id 属性时我的按钮没有点击?

为啥当我点击更新按钮时出现错误TypeError: r is undefined?