在 Swift 中向选择器传递参数

我正在以编程方式向我的一个视图添加一个 UITapGestureIdentiizer:

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’的参数没有引用‘@Objecc’方法、属性或初始化程序。

酷,所以我在 handleTap 签名中添加了@objecc:

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

现在我得到一个错误“ Method can not be mark@objecc,因为这个参数的类型不能在 Objective-C 中表示。

这只是一张建筑物地图的图片,还有一些指示兴趣点位置的图片。当用户点击其中一个引脚时,我想知道他们点击了哪个感兴趣的点,我有一个描述这些感兴趣点的模型对象。我使用这个模型对象来给图钉图像它的坐标在地图上,所以我认为这会很容易,我只是发送对象的手势处理程序。

131325 次浏览

It looks like you're misunderstanding a couple of things.

When using target/action, the function signature has to have a certain form…

func doSomething()

or

func doSomething(sender: Any)

or

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

where…

The sender parameter is the control object sending the action message.

In your case, the sender is the UITapGestureRecognizer

Also, #selector() should contain the func signature, and does NOT include passed parameters. So for…

func handleTap(sender: UIGestureRecognizer) {
    

}

you should have…

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

Assuming the func and the gesture are within a view controller, of which modelObj is a property / ivar, there's no need to pass it with the gesture recogniser, you can just refer to it in handleTap

Step 1: create the custom object of the sender.

step 2: add properties you want to change in that a custom object of the sender

step 3: typecast the sender in receiving function to a custom object and access those properties

For eg: on click of the button if you want to send the string or any custom object then

step 1: create

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

step 2-a: set the custom class in the storyboard as well

enter image description here

step 2-b: Create IBOutlet of that button with a custom class as follows

@IBOutlet weak var btnFullRemote: CustomButton!

step 3: add properties you want to change in that a custom object of the sender

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

step 4: typecast the sender in receiving function to a custom object and access those properties

@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


}

that may be a terrible practice but I simply add whatever I want to restore to

button.restorationIdentifier = urlString

and

@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: [:])
}
}


}

Swift 5.0 iOS 13

I concur a great answer by Ninad. Here is my 2 cents, the same and yet different technique; a minimal version.

Create a custom class, throw a enum to keep/make the code as maintainable as possible.

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


class customTap: UITapGestureRecognizer {
var cutomTag: String?
}

Use it, making sure you set the custom variable into the bargin. Using a simple label here, note the last line, important labels are not normally interactive.

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

And setup the action using it, note I wanted to use the pure enum, but it isn't supported by Objective C, so we go with a basic type, String in this case.

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

And there you have it.

Just create a custom class of 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)
}
}

And then create UIImageView extension =>

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

Now use it like =>

self.userImageView.gotoOtherUserProfile(otherUserModel: OtherUserModel)
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
}

You can use an UIAction instead:

self.imageView.addAction(UIAction(identifier: UIAction.Identifier("imageClick")) { [weak self] action in
self?.handleTap(modelObj)
}, for: .touchUpInside)