在Swift中将参数传递给选择器


76

我正在以编程方式将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
}

现在,我收到错误消息“无法将方法标记为@objc,因为无法在Objective-C中表示参数的类型。

它只是建筑物地图的图像,其中一些图钉图像指示了兴趣点的位置。当用户点击这些引脚之一时,我想知道他们轻按了哪个兴趣点,并且我有一个描述这些兴趣点的模型对象。我使用此模型对象为钉图提供其在地图上的坐标,因此我认为将对象发送到手势处理程序会很容易。


1
您不能将值传递给这样的选择器。为什么不能将该值保留在实例变量中并从选择器方法中访问它?
Midhun MP

Answers:


95

看来您误解了几件事。

使用target / action时,功能签名必须具有某种形式…

func doSomething(sender: Any)

要么

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

哪里…

sender参数是发送操作消息的控制对象。

在您的情况下,发件人是 UITapGestureRecognizer

另外,#selector()应包含func签名,并且不包含传递的参数。因此对于…

func handleTap(sender: UIGestureRecognizer) {

}

你应该有…

let gesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(sender:)))

假设func和手势位于视图控制器中,而视图控制器modelObj是一个属性/ ivar,则无需将其与手势识别器一起传递,您只需在handleTap


1
SwiftUIControl文档的“目标-动作机制”部分也很不错,显示了动作方法可以采用的三种形式。
toraritte '18

为了增加这个很好的答案,某些对象类型(例如Timer)具有userInfo: Any?可与该解决方案结合使用的参数,以与发送方一起传递模型对象。
贾斯汀·瓦利

32

步骤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:也在情节提要中设置自定义类

在此处输入图片说明

步骤2-b:使用自定义类创建该按钮的IBOutlet,如下所示

@IBOutlet weak var btnFullRemote: CustomButton!

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

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

步骤4:将接收函数中的发送方类型转换为自定义对象并访问那些属性

@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

}

2
给出了一个好的解决方案的聪明答案(而不是说这是不可能的,因为我对此已经找到了大多数答案)...尝试了这种方法并可以确认它的有效性
Josh Wolff

1
自定义对象包装器可以传递您所需的一切!好聪明。谢谢。
凯尔·胡德

9

Swift 5.0 iOS 13

我同意由一个伟大的答案Ninad。这是我的2美分,相同但又不同的技术;最低版本。

创建一个自定义类,抛出一个枚举以保持/使代码尽可能可维护。

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

class customTap: UITapGestureRecognizer {
  var cutomTag: String?
}

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

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

并使用它来设置操作,请注意我想使用纯枚举,但是Objective C不支持它,因此在这种情况下,我们使用了基本类型String。

@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
    }
}

那里有。


太棒了 这可行。但我认为@objc func actionB(sender:Any){let tap = sender as吗?customTap开关tap..customTag {...
马都

2

只需创建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)

-1

这可能是一个可怕的做法,但我只是添加要还原的内容

button.restorationIdentifier = urlString

@objc func openRelatedFact(_ sender: Any) {
        if let button = sender as? UIButton, let stringURL = factButton.restorationIdentifier, let url = URL(string: stringURL) {
            if UIApplication.shared.canOpenURL(url) {
                UIApplication.shared.open(url, options: [:])
            }
        }

    }

糟糕的练习……是的。太具有创造力了!工藤的!
SpacyRicochet

-1
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
该问题已在不久前得到解答,并且已经得到了接受。这意味着任何其他答案都应为该问题的任何读者提供新的前提。因此,我建议您在回答中添加更多上下文,并说明为什么它提供了解决问题的独特方法。有关回答问题的更多信息,请看这里:stackoverflow.com/help/how-to-answer
BeWu
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.