Dołącz parametr do akcji button.addTarget w Swift


102

Próbuję przekazać dodatkowy parametr do akcji buttonClicked, ale nie mogę ustalić, jaka powinna być składnia w języku Swift.

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

Dowolna metoda buttonClicked:

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

Czy ktoś ma jakieś pomysły?

Dzięki za pomoc.


1
Skorzystaj z tego łącza, aby uzyskać szczegółową odpowiedź i najlepsze praktyki
— arkuszowy

To pytanie nie ma dobrej odpowiedzi, ponieważ opisuje już problematyczną architekturę. Nie potrzebujesz dodatkowego parametru. Potrzeba dodatkowego parametru jest problemem w Twojej architekturze.
— Sulthan

WARNING TO INTERNET: please check out my answer at the bottom
— Pan Heelis,

Odpowiedzi:


170

Nie możesz przekazać parametrów niestandardowych w addTarget:.Jedną alternatywą jest ustawienie tagwłaściwości przycisku i wykonanie pracy w oparciu o tag.

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

Lub dla Swift 2.2 i nowszych:

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

Teraz wykonaj logikę opartą na tagwłasności

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

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

4
Cześć, jeśli chcę uzyskać indexPath lub komórkę przycisku w tableView, czy jest to możliwe?
— NKurapati

1
Oto wskazówka: nie ustawiaj tej metody jako prywatnej.
— OhadM,

2
Wygląda na to, że jego typem może być tylko plik Int. Nic więcej.
— scaryguy

2
co jeśli chcę przekazać wiersz i sekcję razem?
— Yestay Muratov

3
tak, żebyś wiedział, pomimo 118 głosów za (i wciąż rosnących), jest to zły sposób na zrobienie tego
— pan Heelis,

79

Jeśli chcesz wysłać dodatkowe parametry do metody buttonClicked, na przykład indexPath lub urlString, możesz podklasować UIButton:

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

Upewnij się, że zmienisz klasę przycisku w inspektorze tożsamości na podklasęUIButton. Możesz uzyskać dostęp do parametrów wewnątrz metody buttonClicked za pomocą sender.indexPathlub sender.urlString.

Uwaga: Jeśli twój przycisk znajduje się wewnątrz komórki, możesz ustawić wartość tych dodatkowych parametrów w metodzie cellForRowAtIndexPath (gdzie przycisk jest tworzony).


5
To był jedyny sposób, który zadziałał dla mnie. Ale jestem ciekawy, czy to anty-wzór, czy nie?
— scaryguy

1
Myślę, że to najlepszy sposób
— Duy Hoang

29

Doceniam wszystkich, którzy mówią używaj tagów, ale tak naprawdę musisz rozszerzyć klasę UIButton i po prostu dodać tam obiekt.

Tagi są beznadziejnym sposobem obejścia tego. Rozszerz UIButton w ten sposób (w 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)
    }
}

wtedy może to być połączenie (UWAGA dwukropek „:” w 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"

w końcu złap to wszystko tutaj

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

Robisz to raz i używasz go w całym projekcie (możesz nawet sprawić, by klasa podrzędna miała ogólny „obiekt” i wstawić cokolwiek chcesz do przycisku!). Lub skorzystaj z powyższego przykładu, aby umieścić niewyczerpaną liczbę parametrów klucza / ciągu znaków w przycisku. Naprawdę przydatne do uwzględniania takich rzeczy, jak adresy URL, metodologia potwierdzania wiadomości itp.

SONawiasem mówiąc, ważne jest, aby społeczność zdała sobie z tego sprawę, że przez alarmującą liczbę programistów, którzy nie rozumieją / nie zostali nauczeni / przegapili sens pojęcieobject extensions


10

W przypadku Swift 3.0 możesz użyć następujących

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

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

}

9

Swift 4.2

Wynik:

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

W pliku UIButton+Events.swiftstworzyłem metodę rozszerzenia, UIButtonktóra wiąże a UIControl.Eventz programem obsługi zakończenia o nazwie 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)
    }
}

Powodem, dla którego użyłem niestandardowej klasy do powiązania zdarzenia, jest możliwość usunięcia odwołania później, gdy przycisk jest deintializowany. Zapobiegnie to ewentualnemu wyciekowi pamięci. Nie było to możliwe w ramach UIButtonjego rozszerzenia, ponieważ nie mogę zaimplementować właściwości ani deinitmetody.


2

Jeśli masz pętlę przycisków jak ja, możesz spróbować czegoś takiego

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

Kod Swift 4.0 (znowu zaczynamy)

Wywołana akcja powinna być oznaczona w ten sposób, ponieważ jest to składnia funkcji szybkiej do eksportowania funkcji do obiektywnego języka c.

@objc func deleteAction(sender: UIButton) {
}

utwórz działający przycisk:

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

Dzięki za udostępnienie. To działa, ale oznaczanie go @objc wydaje się nieintuicyjne. Czy możesz wyjaśnić powód tego?
— rawbee

@rawbee Zgadzam się, że jest to sprzeczne z intuicją. To pytanie ma więcej tła: stackoverflow.com/questions/44390378/…
— Mikrasya

1

Kod Swift 5.0

Używam theButton.tag, ale jeśli mam dużo opcji, jest to bardzo długa obudowa przełącznika.

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

.

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

0

Dla Swift 2.X i nowszych

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

0

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

Możesz wysłać wartość w „informacje o użytkowniku” i użyć jej jako parametru w funkcji.


0

To jest bardziej ważny komentarz. Udostępnianie referencji sytanx, które jest dopuszczalne po wyjęciu z pudełka. W przypadku rozwiązań hakerskich spójrz na inne odpowiedzi.

Zgodnie z dokumentacją Apple, definicje metod działania muszą być jedną z tych trzech. Wszystko inne jest niedopuszczalne.

@IBAction func doSomething()
@IBAction func doSomething(sender: UIButton)
@IBAction func doSomething(sender: UIButton, forEvent event: UIEvent)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.