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

我想要

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

我做的是

  1. UIApplication.shared.keyWindow 中安装UITapGestureRecognizer
  2. UIGestureRecognizerDelegate 中,为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 中发布了一个适用于所有情况的全局修饰的简单解决方案。它不符合您的要求吗?还是你没看到

标签: ios swift


【解决方案1】:

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

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

  1. 要使用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()
    }
}
  1. 我更喜欢使用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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-12-05
        • 2020-09-24
        • 2016-12-28
        • 1970-01-01
        • 2022-10-15
        • 2018-01-05
        • 1970-01-01
        相关资源
        最近更新 更多