如何将参数添加到通用 UITapGestureRecognizer?

Posted

技术标签:

【中文标题】如何将参数添加到通用 UITapGestureRecognizer?【英文标题】:How to add parameters to a generic UITapGestureRecognizer? 【发布时间】:2021-06-03 12:32:28 【问题描述】:

问题是:如何制作通用版本的 UITapGestureRecognizer,以便在我的应用程序中使用。

如果我不想传递任何参数,这很简单:

class ClickListener: UITapGestureRecognizer 
    var onClick : (() -> Void)? = nil



// MARK: UIView Extension
extension UIView 
    
    func setOnClickListener(action :@escaping () -> Void)
        let tapRecogniser = ClickListener(target: self, action: #selector(onViewClicked(sender:)))
        tapRecogniser.onClick = action
        self.addGestureRecognizer(tapRecogniser)
    
    
    @objc func onViewClicked(sender: ClickListener) 
        if let onClick = sender.onClick 
            onClick()
        
    
  

正如Sunneet Agrawal 所做的那样。

问题是,我有时需要向这个函数传递参数。 例如,如果我想传递一个对象,我想这样做。然后我可以通过应用程序全局重用这个功能。

我尝试的一切都不起作用,我可以提供一些这样的代码:

class ClickListener<T: Any>: UITapGestureRecognizer 
    var onClick : (() -> Void)? = nil
    var clickedObject: Any
    
    override init(onClick: (() -> Void), clickedObject: Any, target: self, action: ???)
        self.onClick = onClick
        self.clickedObject = clickedObject
        super.init(target: self, action: ???)
    


extension UIView 
    
    func setOnClickListener(action :@escaping () -> Void)
        let tapRecogniser = ClickListener(// Init here)
        tapRecogniser.onClick = action( // parameter here)
        self.addGestureRecognizer(tapRecogniser)
    
    
    @objc func onViewClicked(sender: ClickListener) 
        if let onClick = sender.onClick 
            onClick(// with parameter passed)
        
    
  


// Then in my VC

UIView.setOnclickListener(action: anyFunction, clickedObject: anyObject)

但我有点迷路了。

【问题讨论】:

函数的参数是什么?到点击手势识别器调用的目标/动作?到您添加到自定义手势识别器的“onClick”闭包?两者都有解决方案。 向点击手势识别器添加闭包并让点击手势识别器向其目标发送一个动作,然后在手势识别器上调用闭包对我来说似乎很奇怪。 我尝试在我的问题中添加更多内容,以便更容易理解。需要明确的是,我希望能够将点击手势识别器添加到 UIViews 并将参数传递给它。例如,我希望能够向传递用户对象的 UIView 添加一个点击函数,然后我可以在函数中使用该对象的特定变量 【参考方案1】:

首先,目标/动作模式的selector有两种固定形式:

没有参数的函数 带有 one 参数的函数表示 sender,即触发操作的对象。

但您可以在子类中添加属性并在(自定义)init 方法中传递参数。

子类中的泛型与框架抗争,因此自定义字典(您也可以只使用 Any)是更好的选择。

例如,除了目标和动作之外,该类还有一个 userInfo 字典和一个 onClick 闭包。 init 方法调用super 调用敲击识别器的指定初始化器。

class ClickListener: UITapGestureRecognizer 
    var onClick : (() -> Void)?
    var userInfo: [String:Any]?
    
    init(target: Any?, action: Selector?, userInfo: [String:Any]? = nil, onClick: (() -> Void)? = nil) 
        self.userInfo = userInfo
        self.onClick = onClick
        super.init(target: target, action: action)
    

你可以使用它

extension UIView 
    
    func setOnClickListener(action :@escaping () -> Void)
        let tapRecogniser = ClickListener(target: self, action: #selector(onViewClicked), userInfo: ["message":"Hello World"], onClick: action)
        self.addGestureRecognizer(tapRecogniser)
    
    
    @objc func onViewClicked(_ sender: ClickListener) 
        if let userInfo = sender.userInfo,
            let message = userInfo["message"] as? String 
            print(message)
        
        sender.onClick?()
    
    


一个更通用的实现是在onClick闭包中传递userInfo

class ClickListener: UITapGestureRecognizer 
    var onClick : (([String:Any]?) -> Void)?
    var userInfo: [String:Any]?
    
    init(target: Any?, action: Selector?, userInfo: [String:Any]? = nil, onClick: (([String:Any]?) -> Void)?) 
        self.userInfo = userInfo
        self.onClick = onClick
        super.init(target: target, action: action)
    


extension UIView 
    
    func setOnClickListener(userInfo: [String:Any], action :@escaping ([String:Any]?) -> Void)
        let tapRecogniser = ClickListener(target: self, action: #selector(onViewClicked), userInfo: userInfo, onClick: action)
        self.addGestureRecognizer(tapRecogniser)
    
    
    @objc func onViewClicked(_ sender: ClickListener) 
        sender.onClick?(sender.userInfo)
    
    

【讨论】:

以上是关于如何将参数添加到通用 UITapGestureRecognizer?的主要内容,如果未能解决你的问题,请参考以下文章

如何将通用存储库(或业务逻辑层)注入 ViewModel

Rust宏接受类型与通用参数

如何将通用项目添加到绑定到 WPF 中的集合的 ComboBox

Cordova 深层链接 - 未从通用链接中检测到查询参数

为什么可以将int []作为通用类型参数,但不能将int作为常规类型参数? [关闭]

如何添加通用依赖注入[重复]