90 votes

Passage d'arguments au sélecteur en Swift

Je suis en train d'ajouter par programme un UITapGestureRecognizer à l'une de mes vues :

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
}

Le premier problème que j'ai rencontré était "L'argument de '#selector' ne fait pas référence à une méthode, une propriété ou un initialisateur '@Objc'.

Cool, donc j'ai ajouté @objc à la signature de handleTap :

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

Maintenant, j'obtiens l'erreur "Method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C".

Il s'agit simplement d'une image du plan d'un bâtiment, avec quelques images d'épingles indiquant l'emplacement de points d'intérêt. Lorsque l'utilisateur touche l'une de ces épingles, j'aimerais savoir quel point d'intérêt il a touché, et j'ai un objet modèle qui décrit ces points d'intérêt. J'utilise cet objet modèle pour donner à l'image de l'épingle ses coordonnées sur la carte et je pensais qu'il aurait été facile pour moi de simplement envoyer l'objet au gestionnaire de gestes.

1 votes

Vous ne pouvez pas passer des valeurs à un sélecteur comme ça. Pourquoi ne pouvez-vous pas conserver cette valeur dans une variable d'instance et y accéder à partir de la méthode du sélecteur ?

4 votes

Duplicata possible de stackoverflow.com/questions/35635595/ .

109voto

Ashley Mills Points 10205

Il semble que vous compreniez mal certaines choses.

Lorsque vous utilisez cible/action la signature de la fonction doit avoir une certaine forme

func doSomething(sender: Any)

ou

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

El sender est l'objet de contrôle qui envoie le message d'action.

Dans votre cas, l'expéditeur est le UITapGestureRecognizer

Aussi, #selector() doit contenir la signature de la fonction, et ne doit PAS inclure les paramètres passés. Donc pour

func handleTap(sender: UIGestureRecognizer) {

}

vous auriez dû

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

En supposant que le func et le geste sont dans un contrôleur de vue, dont modelObj est une propriété / ivar, il n'y a pas besoin de la passer avec le reconnaissant de gestes, vous pouvez juste y faire référence dans handleTap

2 votes

Le mécanisme d'action sur la cible de la section Documentation sur UIControl pour Swift est également une bonne lecture qui montre les trois formes que peuvent prendre les méthodes d'action.

0 votes

Pour ajouter à cette excellente réponse, certains types d'objets tels que Timer ont userInfo: Any? qui peuvent être utilisés en conjonction avec cette solution pour transmettre des objets de modèle avec l'expéditeur.

38voto

Ninad Kambli Points 265

Etape 1 : créer l'objet personnalisé de l'expéditeur.

étape 2 : ajoutez les propriétés que vous voulez modifier dans cette a objet personnalisé de l'expéditeur

étape 3 : typecast de l'expéditeur dans la fonction de réception à une objet personnalisé et accéder à ces propriétés

Par ex : si vous voulez envoyer une chaîne de caractères ou un objet personnalisé lors du clic sur le bouton, alors

étape 1 : création

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

étape 2-a : définir la classe personnalisée dans le storyboard également

enter image description here

étape 2-b : Créez l'IBOutlet de ce bouton avec une classe personnalisée comme suit

@IBOutlet weak var btnFullRemote: CustomButton!

étape 3 : ajoutez les propriétés que vous voulez modifier dans cette a objet personnalisé de l'expéditeur

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

étape 4 : typecast de l'expéditeur dans la fonction de réception à une objet personnalisé et accéder à ces propriétés

@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

}

3 votes

Réponse intelligente qui donne une bonne solution (plutôt que de dire que c'est impossible, comme le disent la plupart des réponses que j'ai trouvées à ce sujet)... J'ai essayé cette approche et je peux confirmer qu'elle fonctionne

2 votes

Une enveloppe d'objet personnalisée pour passer tout ce dont vous avez besoin ! Si intelligent. Merci.

16voto

user3069232 Points 3201

Swift 5.0 iOS 13

Je suis d'accord avec une grande réponse de Ninad . Voici mes deux cents, la même technique et pourtant différente ; une version minimale.

Créez une classe personnalisée, lancez un enum pour garder/rendre le code aussi maintenable que possible.

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

class customTap: UITapGestureRecognizer {
  var cutomTag: String?
}

Utilisez-le, en veillant à définir la variable personnalisée dans la marge. Nous utilisons ici une simple étiquette, notez la dernière ligne, les étiquettes importantes ne sont normalement pas interactives.

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

Et configurer l'action en l'utilisant, notez que je voulais utiliser le pur enum, mais il n'est pas supporté par l'Objective C, donc nous allons avec un type de base, String dans ce cas.

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

Et voilà, vous l'avez.

5voto

Amanpreet Singh Points 81
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
}

4voto

Rajan Singh Points 182

Il suffit de créer une classe personnalisée d'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)
    }
}

Et ensuite créer l'extension 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)
        }
    }

Maintenant, utilisez-le comme =>

self.userImageView.gotoOtherUserProfile(otherUserModel: OtherUserModel)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X