Attachez le paramètre à l'action button.addTarget dans Swift


102

J'essaie de passer un paramètre supplémentaire à l'action buttonClicked, mais je ne peux pas déterminer quelle devrait être la syntaxe dans Swift.

button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)

N'importe quelle méthode my buttonClicked:

func buttonClicked(sender:UIButton)
{
    println("hello")
}

Quelqu'un a des idées?

Merci de votre aide.


1
Reportez-vous à ce lien pour une réponse détaillée et les meilleures pratiques
feuille

La question n'a pas de bonne réponse car elle décrit déjà une architecture problématique. Vous n'avez pas besoin d'un paramètre supplémentaire. Le besoin d'un paramètre supplémentaire est un problème dans votre architecture.
Sulthan

WARNING TO INTERNET: please check out my answer at the bottom
Mr Heelis

Réponses:


170

Vous ne pouvez pas transmettre de paramètres personnalisés dans. addTarget:Une alternative est de définir la tagpropriété de bouton et de travailler en fonction de la balise.

button.tag = 5
button.addTarget(self, action: "buttonClicked:", 
    forControlEvents: UIControlEvents.TouchUpInside)

Ou pour Swift 2.2 et supérieur:

button.tag = 5
button.addTarget(self,action:#selector(buttonClicked),
    forControlEvents:.TouchUpInside)

Maintenant, faites de la logique basée sur la tagpropriété

@objc func buttonClicked(sender:UIButton)
{
    if(sender.tag == 5){

        var abc = "argOne" //Do something for tag 5
    }
    print("hello")
}

4
Salut, si je veux obtenir indexPath ou une cellule de bouton dans tableView, est-ce possible?
NKurapati

1
Voici un indice: ne rendez pas la méthode privée.
OhadM

2
On dirait que son type ne peut être qu'un fichier Int. Rien d'autre.
scaryguy

2
et si je veux passer la ligne et la section ensemble?
Yestay Muratov

3
juste pour que vous sachiez, malgré 118 votes positifs (et comptés), ce n'est pas la
bonne

79

Si vous souhaitez envoyer des paramètres supplémentaires à la méthode buttonClicked, par exemple un indexPath ou urlString, vous pouvez sous-classer l'UIButton:

class SubclassedUIButton: UIButton {
    var indexPath: Int?
    var urlString: String?
}

Assurez-vous de changer la classe du bouton dans l'inspecteur d'identité en sous-classeUIButton. Vous pouvez accéder aux paramètres de la méthode buttonClicked en utilisant sender.indexPathousender.urlString .

Remarque: Si votre bouton se trouve à l'intérieur d'une cellule, vous pouvez définir la valeur de ces paramètres supplémentaires dans la méthode cellForRowAtIndexPath (où le bouton est créé).


5
C'était la seule façon qui fonctionnait pour moi. Mais je suis curieux, est-ce anti-pattern ou pas?
scaryguy

1
C'est le meilleur moyen, je pense
Duy Hoang

29

J'apprécie que tout le monde dise utiliser des balises, mais vous devez vraiment étendre la classe UIButton et y ajouter simplement l'objet.

Les tags sont un moyen désespéré de contourner cela. Étendez l'UIButton comme ceci (dans Swift 4)

import UIKit
class PassableUIButton: UIButton{
    var params: Dictionary<String, Any>
    override init(frame: CGRect) {
        self.params = [:]
        super.init(frame: frame)
    }

    required init?(coder aDecoder: NSCoder) {
        self.params = [:]
        super.init(coder: aDecoder)
    }
}

alors votre appel peut être call (NOTEZ LES deux points ":" in Selector(("webButtonTouched:")))

let webButton = PassableUIButton(frame: CGRect(x:310, y:40, width:40, height:40))
webButton.setTitle("Visit",for: .normal)
webButton.addTarget(self, action: #selector(YourViewController.webButtonTouched(_:)), for:.touchUpInside)
webButton.params["myvalue"] = "bob"

puis enfin attraper tout ici

@IBAction func webButtonTouched(_ sender: PassableUIButton) {
    print(sender.params["myvalue"] ?? "")
}

Vous faites cela une fois et l'utilisez tout au long de votre projet (vous pouvez même faire en sorte que la classe enfant ait un "objet" générique et mettre ce que vous voulez dans le bouton!). Ou utilisez l'exemple ci-dessus pour mettre un nombre inépuisable de paramètres clé / chaîne dans le bouton. Vraiment utile pour inclure des choses comme des URL, confirmer la méthodologie de message, etc.

En passant, il est important que la SOcommunauté se rende compte qu'il y a toute une génération de mauvaises pratiques coupées et collées sur Internet par un nombre alarmant de programmeurs qui ne comprennent pas / n'ont pas été enseignés / ont raté le but de le concept deobject extensions


10

Pour Swift 3.0, vous pouvez utiliser

button.addTarget(self, action: #selector(YourViewController.YourMethodName(_:)), for:.touchUpInside)

func YourMethodName(_ sender : UIButton) {
    print(sender.tag)

}

9

Swift 4.2

Résultat:

testButton.on(.touchUpInside) { (sender, event) in
    // You can use any reference initialized before the code block here
    // You can access self by adding [weak self] before (sender, event)
    // You can then either make self strong by using a guard statement or use a optional operator (?)
    print("user did press test button")
}

Dans le fichier, UIButton+Events.swiftj'ai créé une méthode d'extension pour UIButtonqui lie un UIControl.Eventà un gestionnaire d'achèvement appelé EventHandler:

import UIKit

fileprivate var bindedEvents: [UIButton:EventBinder] = [:]

fileprivate class EventBinder {

    let event: UIControl.Event
    let button: UIButton
    let handler: UIButton.EventHandler
    let selector: Selector

    required init(
        _ event: UIControl.Event,
        on button: UIButton,
        withHandler handler: @escaping UIButton.EventHandler
    ) {
        self.event = event
        self.button = button
        self.handler = handler
        self.selector = #selector(performEvent(on:ofType:))
        button.addTarget(self, action: self.selector, for: event)
    }

    deinit {
        button.removeTarget(self, action: selector, for: event)
        if let index = bindedEvents.index(forKey: button) {
            bindedEvents.remove(at: index)
        }
    }
}

private extension EventBinder {

    @objc func performEvent(on sender: UIButton, ofType event: UIControl.Event) {
        handler(sender, event)
    }
}

extension UIButton {

    typealias EventHandler = (UIButton, UIControl.Event) -> Void

    func on(_ event: UIControl.Event, handler: @escaping EventHandler) {
        bindedEvents[self] = EventBinder(event, on: self, withHandler: handler)
    }
}

La raison pour laquelle j'ai utilisé une classe personnalisée pour lier l'événement est de pouvoir supprimer la référence plus tard lorsque le bouton est désintialisé. Cela évitera une éventuelle fuite de mémoire. Cela n'a pas été possible dans l' UIButtonextension its, car je ne suis pas autorisé à implémenter une propriété ni la deinitméthode.


2

Si vous avez une boucle de boutons comme moi, vous pouvez essayer quelque chose comme ça

var buttonTags:[Int:String]? // can be [Int:Any]
let myArray = [0:"a",1:"b"]
for (index,value) in myArray {

     let button = // Create a button

     buttonTags?[index] = myArray[index]
     button.tag = index
     button.addTarget(self, action: #selector(buttonAction(_:)), for: .touchDown)

}
@objc func buttonAction(_ sender:UIButton) {

    let myString = buttonTags[sender.tag]

}

2

Code Swift 4.0 (nous y revoilà)

L'action appelée doit être marquée comme ceci car c'est la syntaxe de la fonction swift pour exporter des fonctions dans un langage objectif c.

@objc func deleteAction(sender: UIButton) {
}

créer un bouton de travail:

let deleteButton = UIButton(type: .roundedRect)
deleteButton.setTitle("Delete", for: [])
deleteButton.addTarget(self, action: #selector( 
MyController.deleteAction(sender:)), for: .touchUpInside)

Merci d'avoir partagé. Cela fonctionne, mais le marquer avec @objc semble peu intuitif. Pouvez-vous expliquer le raisonnement pour cela?
rawbee

@rawbee Convenez que c'est contre-intuitif. Cette question a plus de contexte: stackoverflow.com/questions/44390378/…
Mikrasya

1

Code SWIFT 5.0

J'utilise theButton.tag mais si j'ai beaucoup de type d'option, c'est un boîtier de commutation très long.

theButton.addTarget(self, action: #selector(theFunc), for: .touchUpInside) 
theButton.frame.name = "myParameter"

.

@objc func theFunc(sender:UIButton){ 
print(sender.frame.name)
}

0

Pour Swift 2.X et supérieur

button.addTarget(self,action:#selector(YourControllerName.buttonClicked(_:)),
                         forControlEvents:.TouchUpInside)

0

Code Swift 3.0

self.timer = Timer.scheduledTimer(timeInterval: timeInterval, target: self, selector:#selector(fetchAutocompletePlaces(timer:)), userInfo:[textView.text], repeats: true)

func fetchAutocompletePlaces(timer : Timer) {

  let keyword = timer.userInfo
}

Vous pouvez envoyer une valeur dans 'userinfo' et l'utiliser comme paramètre dans la fonction.


0

C'est plus un commentaire important. Partage de références de sytanx qui est acceptable hors de la boîte. Pour les solutions de piratage, regardez d'autres réponses.

Selon la documentation d'Apple, les définitions de méthode d'action doivent être l'une de ces trois. Tout le reste n'est pas accepté.

@IBAction func doSomething()
@IBAction func doSomething(sender: UIButton)
@IBAction func doSomething(sender: UIButton, forEvent event: UIEvent)
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.