Associa il parametro all'azione button.addTarget in Swift


102

Sto cercando di passare un parametro aggiuntivo all'azione buttonClicked, ma non riesco a capire quale dovrebbe essere la sintassi in Swift.

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

Qualsiasi mio metodo buttonClicked:

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

Qualcuno ha idee?

Grazie per l'aiuto.


1
Fare riferimento a questo collegamento per una risposta dettagliata e le migliori pratiche
foglio

La domanda non ha una buona risposta perché descrive già un'architettura problematica. Non hai bisogno di un parametro aggiuntivo. La necessità di un parametro aggiuntivo è un problema nella tua architettura.
Sulthan

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

Risposte:


170

Non è possibile passare parametri personalizzati in addTarget:. Un'alternativa è impostare la tagproprietà del pulsante e lavorare in base al tag.

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

O per Swift 2.2 e versioni successive:

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

Ora fai la logica basata sulla tagproprietà

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

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

4
Ciao, se voglio ottenere indexPath o la cella del pulsante in tableView, è possibile?
NKurapati

1
Un suggerimento: non rendere privato il metodo.
OhadM

2
Sembra che solo il tipo possa essere un file Int. Nient'altro.
scaryguy

2
cosa succede se voglio passare riga e sezione insieme?
Ieri Muratov

3
solo così sai, nonostante 118 voti positivi (e il conteggio) questo è il modo sbagliato per farlo
Mr Heelis

79

Se vuoi inviare parametri aggiuntivi al metodo buttonClicked, ad esempio indexPath o urlString, puoi sottoclassare UIButton:

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

Assicurati di cambiare la classe del pulsante nell'Identity Inspector in subclassedUIButton. È possibile accedere ai parametri all'interno del metodo buttonClicked utilizzando sender.indexPatho sender.urlString.

Nota: se il pulsante si trova all'interno di una cella, è possibile impostare il valore di questi parametri aggiuntivi nel metodo cellForRowAtIndexPath (dove viene creato il pulsante).


5
Questo è stato l'unico modo che ha funzionato per me. Ma sono curioso, è questo anti-pattern o no?
scaryguy

1
Questo è il modo migliore, penso
Duy Hoang

29

Apprezzo tutti coloro che dicono di usare i tag, ma in realtà è necessario estendere la classe UIButton e aggiungere semplicemente l'oggetto lì ..

I tag sono un modo senza speranza per aggirare questo. Estendi il pulsante UIB in questo modo (in 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)
    }
}

allora la tua chiamata potrebbe essere chiamata (NOTA I due punti ":" 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"

poi finalmente prendi tutto qui

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

Puoi farlo una volta e usarlo durante il tuo progetto (puoi persino fare in modo che la classe figlia abbia un "oggetto" generico e mettere quello che vuoi nel pulsante!). Oppure usa l'esempio sopra per inserire un numero inesauribile di parametri chiave / stringa nel pulsante .. Veramente utile per includere cose come URL, conferma della metodologia dei messaggi ecc.

Per inciso, è importante che la SOcomunità si renda conto che c'è un'intera generazione di cattive pratiche tagliate e incollate su Internet da un numero allarmante di programmatori che non capiscono / non hanno imparato / hanno perso il punto di il concetto diobject extensions


10

Per Swift 3.0 puoi usare quanto segue

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

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

}

9

Swift 4.2

Risultato:

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

Nel file UIButton+Events.swiftho creato un metodo di estensione per UIButtonche lega un UIControl.Eventa un gestore di completamento chiamato 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)
    }
}

Il motivo per cui ho utilizzato una classe personalizzata per l'associazione dell'evento è per poter disporre del riferimento in un secondo momento quando il pulsante viene deintializzato. Ciò impedirà che si verifichi una possibile perdita di memoria. Questo non è stato possibile all'interno della UIButtonsua estensione, perché non sono autorizzato a implementare una proprietà né il deinitmetodo.


2

Se hai un ciclo di pulsanti come me, puoi provare qualcosa di simile

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

Codice Swift 4.0 (ci risiamo)

L'azione richiamata dovrebbe essere contrassegnata in questo modo perché questa è la sintassi per la funzione swift per esportare funzioni nel linguaggio oggettivo c.

@objc func deleteAction(sender: UIButton) {
}

creare un pulsante funzionante:

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

Grazie per la condivisione. Funziona, ma contrassegnarlo con @objc non sembra intuitivo. Puoi spiegare il ragionamento di questo?
rawbee

@rawbee Concordo sul fatto che questo è controintuitivo. Questa domanda ha più di fondo: stackoverflow.com/questions/44390378/...
Mikrasya

1

Codice Swift 5.0

Uso theButton.tag ma se ho molti tipi di opzioni, è molto lungo.

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

.

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

0

Per Swift 2.X e versioni successive

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

0

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

È possibile inviare un valore in "userinfo" e utilizzarlo come parametro nella funzione.


0

Questo è più un commento importante. Condivisione di riferimenti di sytanx accettabili fuori dagli schemi. Per le soluzioni di hack, guarda altre risposte.

Secondo i documenti di Apple, le definizioni del metodo di azione devono essere una di queste tre. Qualsiasi altra cosa non è accettata.

@IBAction func doSomething()
@IBAction func doSomething(sender: UIButton)
@IBAction func doSomething(sender: UIButton, forEvent event: UIEvent)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.