在Swift中向选择器传递参数

108

我正在通过编程向我的一个视图添加一个UITapGestureRecognizer:

let gesture = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(modelObj:myModelObj)))

self.imageView.addGestureRecognizer(gesture)

func handleTap(modelObj: Model) {
  // Doing stuff with model object here
}

我遇到的第一个问题是“'#selector' 的参数不是 '@Objc' 方法、属性或初始化器。”

很好,所以我在 handleTap 签名上添加了 @objc 标记:

@objc func handleTap(modelObj: Model) {
  // Doing stuff with model object here
}

现在我收到了错误消息:“Method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C.”

这只是一个建筑物地图的图片,上面有一些标识兴趣点位置的图钉图片。当用户点击其中一个图钉时,我想知道他们点击了哪个兴趣点,我有一个描述这些兴趣点的模型对象。我使用此模型对象来为图钉图片在地图上分配其坐标,因此我认为将对象发送到手势处理程序会很容易。


1
你不能这样传递值给选择器。为什么不将该值保存在实例变量中并从选择器方法中访问它呢? - Midhun MP
4
可能是https://dev59.com/YJTfa4cB1Zd3GeqPRHmw的重复问题。 - Martin R
6个回答

121

看起来你对一些事情有误解。

在使用target/action时,函数签名必须具有特定的形式...

func doSomething()
或者
func doSomething(sender: Any)
或者
func doSomething(sender: Any, forEvent event: UIEvent)

其中,sender参数是发送动作消息的控件对象。

在您的情况下,发送者是UITapGestureRecognizer

另外,#selector()应该包含函数签名,而不包括传递的参数。所以对于...

func handleTap(sender: UIGestureRecognizer) {
    
}

你应该已经……

let gesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(sender:)))
假设在一个视图控制器中,modelObj 是一个属性/实例变量,假设 func 和手势都在这个视图控制器中,那么在手势识别器中不需要传递它,你可以在 handleTap 方法中直接引用它。

2
UIControl文档中的“Target-Action Mechanism”部分也是一个不错的阅读材料,展示了动作方法可以采取的三种形式。同时,该文档还适用于Swift语言。 - toraritte
3
为了补充这个伟大的答案,某些对象类型,如计时器,具有可与此解决方案一起使用的“userInfo: Any?”参数,以传递带有发送方模型对象。 - Justin Vallely
@JustinVallely 这真的是一个很棒的回答!谢谢你! :) - Aviram Netanel
不是一个好的回答。你没有解释如何使用发送者。 - 6rchid

41

步骤1:创建发送者的自定义对象。

步骤2:在发送者的自定义对象中添加要更改的属性。

步骤3:在接收函数中将发送者强制转换为自定义对象,并访问这些属性。

例如: 如果您想要在点击按钮时发送字符串或任何自定义对象,则

步骤1:创建

class CustomButton : UIButton {

    var name : String = ""
    var customObject : Any? = nil
    var customObject2 : Any? = nil

    convenience init(name: String, object: Any) {
        self.init()
        self.name = name
        self.customObject = object
    }
}

步骤2-a:在Storyboard中设置自定义类

输入图像描述

步骤2-b:创建以下具有自定义类的按钮的IBOutlet

@IBOutlet weak var btnFullRemote: CustomButton!

步骤三:在发件人的自定义对象中添加您想要更改的属性

btnFullRemote.name = "Nik"
btnFullRemote.customObject = customObject
btnFullRemote.customObject2 = customObject2
btnFullRemote.addTarget(self, action: #selector(self.btnFullRemote(_:)), for: .touchUpInside)

第四步:将发送方在接收函数中强制转换为一个自定义对象,并访问这些属性。

@objc public func btnFullRemote(_ sender: Any) {

var name : String = (sender as! CustomButton).name as? String

var customObject : customObject = (sender as! CustomButton).customObject as? customObject

var customObject2 : customObject2 = (sender as! CustomButton).customObject2 as? customObject2

}

4
聪明的回答给出了一个好的解决方案(而不是像我找到的大多数答案那样说这是不可能的)... 我尝试了这种方法并确认它可行。 - Joshua Wolff
2
自定义对象包装器传递您所需的一切!太聪明了。谢谢。 - Kyle Beard

17

Swift 5.0 iOS 13

我同意Ninad的好答案。这是我的意见,与之前的方法相同但也有所不同;这是一个最简版本。

创建一个自定义类,使用枚举来尽可能保持代码的可维护性。

enum Vs: String {
  case pulse = "pulse"
  case precision = "precision"
} 

class customTap: UITapGestureRecognizer {
  var cutomTag: String?
}

使用它,确保将自定义变量设置为bargin。在这里使用一个简单的标签,注意最后一行,重要标签通常不会交互。

let precisionTap = customTap(target: self, action: #selector(VC.actionB(sender:)))
precisionTap.customTag = Vs.precision.rawValue
precisionLabel.addGestureRecognizer(precisionTap)
precisionLabel.isUserInteractionEnabled = true

然后使用它设置操作,注意我想要使用纯枚举,但是Objective C不支持它,所以我们使用基本类型,在这种情况下为字符串。

@objc func actionB(sender: Any) {
// important to cast your sender to your cuatom class so you can extract your special setting.
  let tag = customTag as? customTap
  switch tag?.sender {
    case Vs.pulse.rawValue:
      // code
    case Vs.precision.rawValue:
      // code
    default:
      break
    }
}

就是这样了。


1
太棒了,这个可行。但我认为应该更正一下: @objc func actionB(sender: Any) { let tap = sender as? customTap switch tap?.customTag { ... - madu

10
cell.btn.tag = indexPath.row //setting tag
cell.btn.addTarget(self, action: #selector(showAlert(_ :)), for: .touchUpInside)

@objc func showAlert(_ sender: UIButton){
  print("sender.tag is : \(sender.tag)")// getting tag's value
}

4

只需创建一个自定义的UITapGestureRecognizer类 =>

import UIKit

class OtherUserProfileTapGestureRecognizer: UITapGestureRecognizer {

    let userModel: OtherUserModel    
    init(target: AnyObject, action: Selector, userModel: OtherUserModel) {
        self.userModel = userModel
        super.init(target: target, action: action)
    }
}

然后创建 UIImageView 扩展 =>

import UIKit

    extension UIImageView {
    
        func gotoOtherUserProfile(otherUserModel: OtherUserModel) {
            isUserInteractionEnabled = true
            let gestureRecognizer = OtherUserProfileTapGestureRecognizer(target: self, action: #selector(self.didTapOtherUserImage(_:)), otherUserModel: otherUserModel)
            addGestureRecognizer(gestureRecognizer)
        }
        
        @objc internal func didTapOtherUserImage(_ recognizer: OtherUserProfileTapGestureRecognizer) {
            Router.shared.gotoOtherUserProfile(otherUserModel: recognizer.otherUserModel)
        }
    }

现在像这样使用它 =>

self.userImageView.gotoOtherUserProfile(otherUserModel: OtherUserModel)

2
您可以使用一个UIAction代替:
self.imageView.addAction(UIAction(identifier: UIAction.Identifier("imageClick")) { [weak self] action in
    self?.handleTap(modelObj)
}, for: .touchUpInside)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接