Set Action Listener Programmatically in Swift
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")
}
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
}
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
}