Прикрепите параметр к действию button.addTarget в Swift


104

Я пытаюсь передать дополнительный параметр действию buttonClicked, но не могу понять, какой синтаксис должен быть в Swift.

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

Любой мой метод buttonClicked:

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

У кого-нибудь есть идеи?

Спасибо за вашу помощь.


1
Подробный ответ и лучшие практики можно найти по этой ссылке
Sheetal

На этот вопрос нет хорошего ответа, потому что он уже описывает проблемную архитектуру. Вам не нужен дополнительный параметр. Необходимость в дополнительном параметре - проблема вашей архитектуры.
Султан

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

Ответы:


170

Вы не можете передавать пользовательские параметры в. addTarget:Одна альтернатива - установить tagсвойство кнопки и работать на основе тега.

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

Или для Swift 2.2 и выше:

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

Теперь выполните логику на основе tagсвойства

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

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

4
Привет, если я хочу получить indexPath или ячейку кнопки в tableView, возможно ли это?
NKurapati

1
Подсказка: не делайте метод закрытым.
OhadM

2
Похоже, что это только тип Int. Ничего больше.
scaryguy

2
что, если я хочу передать строку и раздел вместе?
Естай Муратов

3
просто чтобы вы знали, несмотря на 118 голосов (и их подсчет), это неправильный способ сделать это
мистер Хилис

79

Если вы хотите отправить дополнительные параметры методу buttonClicked, например indexPath или urlString, вы можете создать подкласс UIButton:

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

Обязательно измените класс кнопки в инспекторе удостоверений на subclassedUIButton. Вы можете получить доступ к параметрам внутри метода buttonClicked с помощью sender.indexPathили sender.urlString.

Примечание. Если ваша кнопка находится внутри ячейки, вы можете установить значение этих дополнительных параметров в методе cellForRowAtIndexPath (где создается кнопка).


5
Это был единственный способ, который сработал для меня. Но мне любопытно, это антипаттерн или нет?
scaryguy

1
Думаю, это лучший способ
Дуй Хоанг

29

Я ценю всех, кто говорит об использовании тегов, но на самом деле вам нужно расширить класс UIButton и просто добавить туда объект.

Теги - безнадежный способ обойти это. Расширьте UIButton следующим образом (в 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)
    }
}

тогда ваш звонок может быть call (ЗАПОМНИТЕ Двоеточие ":" в 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"

тогда, наконец, поймай все это здесь

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

Вы делаете это один раз и используете в своем проекте (вы даже можете сделать дочерний класс общим «объектом» и поместить все, что хотите, в кнопку!). Или используйте приведенный выше пример, чтобы поместить в кнопку неисчерпаемое количество параметров ключа / строки. Действительно полезно для включения таких вещей, как URL-адреса, методика подтверждения сообщений и т. Д.

Кроме того, важно, чтобы SOсообщество осознало, что существует целое поколение плохой практики, которую вырезают в Интернете тревожным числом программистов, которые не понимают / не учили / не понимают сути Концепция чего-либоobject extensions


10

Для Swift 3.0 вы можете использовать следующие

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

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

}

9

Swift 4.2

Результат:

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

В этом файле UIButton+Events.swiftя создал метод расширения, UIButtonкоторый связывает a UIControl.Eventс обработчиком завершения, который называется 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)
    }
}

Причина, по которой я использовал настраиваемый класс для привязки события, заключается в том, чтобы иметь возможность удалить ссылку позже, когда кнопка будет деинициализирована. Это предотвратит возможную утечку памяти. Это было невозможно вUIButton его расширении, потому что мне не разрешено реализовывать свойство или deinitметод.


2

Если у вас есть петля кнопок, как у меня, вы можете попробовать что-то вроде этого

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

Код Swift 4.0 (здесь мы снова)

Вызываемое действие должно быть помечено таким образом, потому что это синтаксис быстрой функции для экспорта функций в целевой язык c.

@objc func deleteAction(sender: UIButton) {
}

создать рабочую кнопку:

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

Спасибо, что поделился. Это работает, но пометить его с помощью @objc кажется неинтуитивным. Вы можете объяснить причину этого?
rawbee

@rawbee Согласитесь, это нелогично. У этого вопроса больше предыстории: stackoverflow.com/questions/44390378/…
Mikrasya 09

1

Код Swift 5.0

Я использую theButton.tag, но если у меня много вариантов, это будет очень длинный случай переключения.

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

.

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

0

Для Swift 2.X и выше

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

0

Код 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
}

Вы можете отправить значение в userinfo и использовать его как параметр в функции.


0

Это более важный комментарий. Совместное использование ссылок на sytanx, что допустимо из коробки. Чтобы найти решения для взлома, посмотрите другие ответы.

Согласно документам Apple, определения методов действий должны быть одним из этих трех. Все остальное не принимается.

@IBAction func doSomething()
@IBAction func doSomething(sender: UIButton)
@IBAction func doSomething(sender: UIButton, forEvent event: UIEvent)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.