Adjuntar parámetro a button.addTarget action en Swift


102

Estoy tratando de pasar un parámetro adicional a la acción buttonClicked, pero no puedo averiguar cuál debería ser la sintaxis en Swift.

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

Cualquiera de mi método buttonClicked:

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

¿Alguien alguna idea?

Gracias por tu ayuda.


1
Consulte este enlace para obtener una respuesta detallada y las mejores prácticas
hoja del

La pregunta no tiene una buena respuesta porque ya describe una arquitectura problemática. No necesita un parámetro adicional. La necesidad de un parámetro adicional es un problema en su arquitectura.
Sulthan

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

Respuestas:


170

No puede pasar parámetros personalizados en addTarget:. Una alternativa es establecer la tagpropiedad del botón y hacer el trabajo basado en la etiqueta.

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

O para Swift 2.2 y superior:

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

Ahora haz la lógica basada en la tagpropiedad

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

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

4
Hola, si quiero obtener indexPath o celda del botón en tableView, ¿es posible?
NKurapati

1
Aquí tienes una pista: no hagas que el método sea privado.
OhadM

2
Parece que su tipo solo puede ser un Int. Nada más.
scaryguy

2
¿Qué pasa si quiero pasar fila y sección juntas?
Yestay Muratov

3
para que lo sepas, a pesar de 118 votos a favor (y contando), esta es la forma incorrecta de hacer esto
Mr Heelis

79

Si desea enviar parámetros adicionales al método buttonClicked, por ejemplo, indexPath o urlString, puede crear una subclase del UIButton:

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

Asegúrese de cambiar la clase del botón en el inspector de identidad a subclassedUIButton. Puede acceder a los parámetros dentro del método buttonClicked usando sender.indexPathosender.urlString .

Nota: Si su botón está dentro de una celda, puede establecer el valor de estos parámetros adicionales en el método cellForRowAtIndexPath (donde se crea el botón).


5
Esta fue la única forma que funcionó para mí. Pero tengo curiosidad, ¿esto es anti-patrón o no?
scaryguy

1
Esta es la mejor manera, creo
Duy Hoang

29

Aprecio que todos digan que use etiquetas, pero realmente necesita extender la clase UIButton y simplemente agregar el objeto allí ...

Las etiquetas son una forma desesperada de evitar esto. Extienda el UIButton así (en 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)
    }
}

entonces su llamada puede ser llamada (NOTA LOS dos puntos ":" en 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"

luego, finalmente, cógelo todo aquí

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

Puede hacer esto una vez y usarlo en todo su proyecto (¡incluso puede hacer que la clase secundaria tenga un "objeto" genérico y poner lo que quiera en el botón!). O use el ejemplo anterior para poner una cantidad inagotable de parámetros de clave / cadena en el botón. Realmente útil para incluir cosas como URL, confirmar metodología de mensajes, etc.

Aparte, es importante que la SOcomunidad se dé cuenta de que hay toda una generación de malas prácticas que están siendo cortadas y pegadas en Internet por un número alarmante de programadores que no entienden / no se les ha enseñado / han perdido el punto de el concepto deobject extensions


10

Para Swift 3.0 puede usar lo siguiente

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

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

}

9

Rápido 4.2

Resultado:

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

En el archivo UIButton+Events.swift, he creado un método de extensión para UIButtonvincular UIControl.Eventa un controlador de finalización llamado 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 razón por la que utilicé una clase personalizada para vincular el evento es poder eliminar la referencia más adelante cuando se desintializa el botón. Esto evitará que se produzca una posible pérdida de memoria. Esto no fue posible dentro de UIButtonsu extensión, porque no puedo implementar una propiedad ni el deinitmétodo.


2

Si tienes un bucle de botones como yo, puedes probar algo como esto

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

Código Swift 4.0 (aquí vamos de nuevo)

La acción llamada debe marcarse así porque esa es la sintaxis de la función rápida para exportar funciones al lenguaje objetivo c.

@objc func deleteAction(sender: UIButton) {
}

crear algún botón de trabajo:

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

Gracias por compartir. Esto funciona, pero marcarlo con @objc no parece intuitivo. ¿Puede explicar el razonamiento de esto?
rawbee

@rawbee Estoy de acuerdo en que esto es contradictorio. Esta pregunta tiene más antecedentes: stackoverflow.com/questions/44390378/…
Mikrasya

1

Código Swift 5.0

Yo uso theButton.tag pero si tengo muchas opciones, es una caja de cambio muy larga.

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

.

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

0

Para Swift 2.X y superior

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

0

Código 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
}

Puede enviar valor en 'userinfo' y usarlo como parámetro en la función.


0

Este es un comentario más importante. Compartir referencias de sytanx que son aceptables desde el primer momento. Para soluciones de pirateo, busque otras respuestas.

Según los documentos de Apple, las definiciones de métodos de acción deben ser una de estas tres. Cualquier otra cosa no se acepta.

@IBAction func doSomething()
@IBAction func doSomething(sender: UIButton)
@IBAction func doSomething(sender: UIButton, forEvent event: UIEvent)
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.