71

I saw some example codes that assign the same OnClick event to all the buttons in Android (even if they perform completely different action) . How can do it with Swift

Android Example:

@Override 
public void onCreate(Bundle savedInstanceState) {
        button1.setOnClickListener(onClickListener);
        button2.setOnClickListener(onClickListener);
        button3.setOnClickListener(onClickListener);
} 

private OnClickListener onClickListener = new OnClickListener() {
     @Override 
     public void onClick(View v) {
         switch(v.getId()){
             case R.id.button1:
                  //DO something 
             break; 
             case R.id.button2:
                  //DO something 
             break; 
             case R.id.button3:
                  //DO something 
             break; 
         } 

   } 
}; 

Note: I don't want create the button programatically.

Gilberto Ibarra
  • 2,849
  • 2
  • 27
  • 38
  • 1
    possible duplicate of [How create button programmatically in Swift?](http://stackoverflow.com/questions/24030348/how-create-button-programmatically-in-swift) – chris Dec 09 '14 at 04:04
  • 5
    No... i need add the same listener to multiple buttons... this is not duplicate. – Gilberto Ibarra Dec 09 '14 at 04:06
  • 1
    call the addTarget method on each button pointing to the same action? – chris Dec 09 '14 at 04:08
  • 1
    UIcontrol has a method -addTarget which can be pointed to the same "listener" [UIControl](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIControl_Class/index.html#//apple_ref/occ/instm/UIControl/addTarget:action:forControlEvents:). But judging from your code why not have 4 listeners instead of one which determines which one of the buttons was pressed? – cream-corn Dec 09 '14 at 04:10
  • @cream-corn Because you may have a dynamic table structure with buttons that can be mapped using row index. – Marcin D Apr 18 '19 at 18:33

3 Answers3

96

On iOS, you're not setting a listener; you add a target (an object) and an action (method signature, "selector" in iOS parlance) to your UIControl (which UIButton is a subclass of):

button1.addTarget(self, action: "buttonClicked:", for: .touchUpInside)
button2.addTarget(self, action: "buttonClicked:", for: .touchUpInside)
button3.addTarget(self, action: "buttonClicked:", for: .touchUpInside)

The first parameter is the target object, in this case self. The action is a selector (method signature) and there are basically two options (more on that later). The control event is a bit specific to the UIControl - .TouchUpInside is commonly used for tapping a button.

Now, the action. That's a method (the name is your choice) of one of the following formats:

func buttonClicked()
func buttonClicked(_ sender: AnyObject?)

To use the first one use "buttonClicked", for the second one (which you want here) use "buttonClicked:" (with trailing colon). The sender will be the source of the event, in other words, your button.

func buttonClicked(_ sender: AnyObject?) {
  if sender === button1 {
    // do something
  } else if sender === button2 {
    // do something
  } else if sender === button3 {
    // do something
  }
}

(this assumes that button1, button2 and button3 are instance variables).

Instead of this one method with the big switch statement consider using separate methods for each button. Based on your specific use case either approach might be better:

func button1Clicked() {
  // do something
}

func button2Clicked() {
  // do something
}

func button3Clicked() {
  // do something
}

Here, I'm not even using the sender argument because I don't need it.

P.S.: Instead of adding targets and actions programmatically you can do so in your Storyboard or nib file. In order to expose the actions you put IBAction in front of your function, e.g.:

@IBAction func button1Clicked() {
  // do something
}
Archy Will He 何魏奇
  • 9,589
  • 4
  • 34
  • 50
Thomas Müller
  • 15,565
  • 6
  • 41
  • 47
76

Swift 4.*

button.addTarget(self, action: #selector(didButtonClick), for: .touchUpInside)

And the button triggers this function:

@objc func didButtonClick(_ sender: UIButton) {
    // your code goes here
}
Gilad Brunfman
  • 3,452
  • 1
  • 29
  • 29
  • 1
    should now be written as #selector(buttonClicked(sender:)) or use #selector(buttonClicked(_:)) btnAll.addTarget(self, action: #selector(buttonClicked(_:)), for: .touchUpInside) then change your function like, func buttonClicked(_ sender : UIButton){ .... } – Vitaliy A Sep 23 '17 at 15:51
  • 4
    how do I check which button was clicked if I set the same function for all buttons? – user25 Feb 12 '18 at 11:20
  • To check which button was clicked you set a different tag when creating them. _button.tag = 1_ (or 2 or 3). Then you use: _switch sender.tag {case 1:..._ – user2888102 Apr 04 '20 at 15:22
3

An Swift 5 Extension Solution

Create A SwiftFile "SetOnClickListener.swift"
copy paste this code

import UIKit

class ClosureSleeve {
  let closure: () -> ()

  init(attachTo: AnyObject, closure: @escaping () -> ()) {
    self.closure = closure
    objc_setAssociatedObject(attachTo, "[\(arc4random())]", self, .OBJC_ASSOCIATION_RETAIN)
  }

  @objc func invoke() {
    closure()
  }
}

extension UIControl {
    func setOnClickListener(for controlEvents: UIControl.Event = .primaryActionTriggered, action: @escaping () -> ()) {
        let sleeve = ClosureSleeve(attachTo: self, closure: action)
        addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: controlEvents)
    }
}

How To use
for example buttonA is a UIButton

buttonA.setOnClickListener {
  print("button A clicked")                  
}
Muhammad Asyraf
  • 1,748
  • 15
  • 20
  • how to get the target element such as in "sender" in @objc func didButtonClick(_ sender: UIButton) { // your code goes here } ? – Nasib Mar 21 '22 at 13:20