By iphaaw


2014-07-17 22:35:58 8 Comments

I am trying to pass an extra parameter to the buttonClicked action, but cannot work out what the syntax should be in Swift.

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

Any my buttonClicked method:

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

Anyone any ideas?

Thanks for your help.

10 comments

@Lloyd Keijzer 2018-12-14 11:42:20

Swift 4.2

Result:

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

In the file UIButton+Events.swift I've created an extension method for UIButton that binds a UIControl.Event to a completion handler called 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)
    }
}

The reason why I used a custom class for binding the event is to be able to dispose the reference later when the button is deintialised. This will prevent a possible memory leak from occurring. This wasn't possible within the UIButton its extension, because I'm not allowed to implement a property nor the deinit method.

@Karsten 2017-10-17 11:21:14

Swift 4.0 code (Here we go again)

The called action should marked like this because that is the syntax for swift function for exporting functions into objective c language.

@objc func deleteAction(sender: UIButton) {
}

create some working button:

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

@rawbee 2017-11-14 15:39:29

Thanks for sharing. This works, but marking it with @objc feels unintuitive. Can you explain the reasoning for this?

@Mikrasya 2018-06-09 21:50:09

@rawbee Agree that this is counterintuitive. This question has more background: stackoverflow.com/questions/44390378/…

@Mr Heelis 2017-10-25 13:55:16

I appreciate everyone saying use tags, but really you need to extend the UIButton class and simply add the object there..

Tags are a hopeless way round this. Extend the UIButton like this (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)
    }
}

then your call may be call (NOTE THE colon ":" 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"

then finally catch it all here

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

You do this one time and use it throughout your project (you can even make the child class have a generic "object" and put whatever you like into the button!). Or use the example above to put an inexhaustible number of key/string params into the button.. Really useful for including things like urls, confirm message methodology etc

As an aside, it's important that the SO community realise this there is an entire generation of bad practice being cut'n'paste round the internet by an alarming number of programmers who don't understand/haven't been taught/missed the point of the concept of object extensions

@yogipriyo 2019-01-10 08:43:49

I am really grateful for this since it allows me pass complex item. Thank you!!!

@brahimm 2017-07-21 09:41:38

If you have a loop of buttons like me you can try something like this

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]

}

@codester 2014-07-17 23:08:08

You cannot pass custom parameters in addTarget:.One alternative is set the tag property of button and do work based on the tag.

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

Or for Swift 2.2 and greater:

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

Now do logic based on tag property

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

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

@NKurapati 2015-06-04 10:10:05

Hi, If i want to get indexPath or cell of button in tableView, is it possible?

@OhadM 2016-04-10 07:31:03

Here's a hint: Don't make the method private.

@scaryguy 2016-08-04 22:29:20

Seems like it's type only can be an Int. Nothing else.

@Yestay Muratov 2016-11-15 06:33:38

what if I want to pass row and section together?

@Mr Heelis 2018-01-05 09:54:12

just so you know, despite 118 upvotes (and counting) this is the wrong way to do this

@krish 2017-03-23 11:14:46

Swift 3.0 code

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
}

You can send value in 'userinfo' and use that as parameter in the function.

@MK_iOS 2016-12-27 12:30:53

For Swift 3.0 you can use following

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

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

}

@Riajur Rahman 2016-12-27 11:28:25

In Swift 3 make a selector like that:

button.addTarget(self, action: #selector(ViewController.multipleParamSelector(_:secondParams:)), for: .touchUpInside)

And catch the event like that:

func multipleParamSelector(_ sender: AnyObject, secondParams: AnyObject) {

}

@salo.dm 2017-08-10 22:08:44

So now you have a selector with a second parameter. But how do you actually send the second parameter? In other words, this shows a selector that can send any object as a second parameter. What is the code that determines which object is sent?

@Maziyar 2018-03-09 13:24:12

it is impossible to guess how to send the second parameter! Can you update the answer with an example, please?

@Dheeraj D 2016-09-13 13:27:27

For Swift 2.X and above

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

@uclagamer 2015-10-08 02:09:27

If you want to send additional parameters to the buttonClicked method, for example an indexPath or urlString, you can subclass the UIButton:

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

Make sure to change the button's class in the identity inspector to subclassedUIButton. You can access the parameters inside the buttonClicked method using sender.indexPath or sender.urlString.

Note: If your button is inside a cell you can set the value of these additional parameters in the cellForRowAtIndexPath method (where the button is created).

@scaryguy 2016-08-05 19:01:29

This was the only way that worked for me. But I'm curious, is this anti-pattern or not?

@Duy Hoang 2018-06-22 04:33:41

This is the best way, I think

Related Questions

Sponsored Content

13 Answered Questions

[SOLVED] Swift for loop: for index, element in array?

  • 2014-06-04 03:19:21
  • metaphy
  • 345317 View
  • 674 Score
  • 13 Answer
  • Tags:   arrays swift

22 Answered Questions

[SOLVED] @selector() in Swift?

9 Answered Questions

[SOLVED] How to Correctly handle Weak Self in Swift Blocks with Arguments

17 Answered Questions

[SOLVED] #pragma mark in Swift?

  • 2014-06-03 14:05:56
  • Arbitur
  • 201019 View
  • 868 Score
  • 17 Answer
  • Tags:   swift

9 Answered Questions

[SOLVED] Swift Beta performance: sorting arrays

15 Answered Questions

[SOLVED] How to call Objective-C code from Swift

  • 2014-06-02 20:05:42
  • David Mulder
  • 258167 View
  • 885 Score
  • 15 Answer
  • Tags:   objective-c swift

33 Answered Questions

[SOLVED] How do you use String.substringWithRange? (or, how do Ranges work in Swift?)

  • 2014-06-04 18:25:20
  • Rob
  • 214632 View
  • 254 Score
  • 33 Answer
  • Tags:   swift

30 Answered Questions

[SOLVED] Using a dispatch_once singleton model in Swift

19 Answered Questions

[SOLVED] Xcode 8 Beta 3 Use Legacy Swift issue

1 Answered Questions

[SOLVED] Swift - alert action handler error

Sponsored Content