Use closure as button's action

swift closure
uibarbuttonitem closure
how to properly do buttons in table view cells using swift closures
argument of '#selector' does not refer to an '@objc' method, property, or initializer
swift selector closure
swift button action
swift trailing closure
uibutton programmatically swift

Background

I have to create a few date-time pickers throughout my app so I was thinking of creating a Utility class that will return a reusable UIDateTimePicker which I will use throughout my app. Each picker will have a Cancel and Done button in the toolbar

Problem For the action that should happen on tapping the Cancel and Done, I was thinking of making use of closures like such:

func createDatePicker(isDateTime: Bool, doneAction: ()->(), cancelAction: ()->()) -> UIDatePicker {

    // Code for setting up the dateTime picker

    // Deciding the picker mode for the picker
    dateTimePicker.datePickerMode = isDateTime ? UIDatePicker.Mode.dateAndTime : UIDatePicker.Mode.date

    // Creating a toolbar for the Picker

    let toolbar = UIToolbar()
    toolbar.sizeToFit()

    // Creating buttons for toolbar
    let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.done, target: self, action: #selector(doneAction)) // ---- ERROR -----

    let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)

    let cancelButton = UIBarButtonItem(title: "Cancel", style: UIBarButtonItemStyle.plain, target: self, action: #selector(cancelAction)) // ---- ERROR -----

    // Adding buttons to the toolbar
    toolbar.setItems([cancelButton,spaceButton,doneButton], animated: false)

    // Adding the Toolbar to the dateTimePicker and returning the dateTimePicker

}

Now, the problem arises when I try to add the closure as the action for the button since they are not selectors(with @objc attached to it)

The error that I get is

Argument of '#selector' cannot refer to parameter 'doneAction'

How to work around this or is there another way to pass the code to be executed(on Cancel and Done) from the main(ViewController) class to this utility class


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

    init (_ closure: @escaping ()->()) {
        self.closure = closure
    }

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

extension UIBarButtonItem {
    public convenience init(title: String?, style: UIBarButtonItem.Style, action: @escaping ()->()) {
        let handle = Closure(action)
        self.init(title: title, style: style, target: handle, action: #selector(Closure.action))
        objc_setAssociatedObject(self, "ex_action", handle, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
    }
}
let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItem.Style.done, action: doneAction) 

Adding Closures to Buttons in Swift, One of the things I still find a little backward and unnatural to use in Swift is the Target-Action pattern. UIButton is in desperate need of a closure. in targetClosure any time the action is triggered, such as tapping the button. Beginning of a dialog window, including tabbed navigation to register an account or sign in to an existing account. Both registration and sign in support using google and facebook


If I understood you correctly you can resolve this issue by adding delegate to your class. Here is some example of how to do it. Hope it helps

    protocol DoneCancelAction {

    func doneAction()
    func cancelAction()

}

class DateTimePicker: UIDatePicker {

    var dateTimePicker: DateTimePicker!
    var delegate: DoneCancelAction? = nil

    func createDatePicker(isDateTime: Bool) -> UIDatePicker {

        // Code for setting up the dateTime picker

        // Deciding the picker mode for the picker
        dateTimePicker.datePickerMode = isDateTime ? UIDatePicker.Mode.dateAndTime : UIDatePicker.Mode.date

        // Creating a toolbar for the Picker

        let toolbar = UIToolbar()
        toolbar.sizeToFit()

        // Creating buttons for toolbar
        let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItem.Style.done, target: self, action: #selector(doneAction)) // ---- ERROR -----

        let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil)

        let cancelButton = UIBarButtonItem(title: "Cancel", style: UIBarButtonItem.Style.plain, target: self, action: #selector(cancelAction)) // ---- ERROR -----

        // Adding buttons to the toolbar
        toolbar.setItems([cancelButton,spaceButton,doneButton], animated: false)

        // Adding the Toolbar to the dateTimePicker and returning the dateTimePicker


        return dateTimePicker

    }

    @objc func doneAction() {
        delegate?.doneAction()
    }

    @objc func cancelAction() {
        delegate?.cancelAction()
    }

}

class MyVC: UIViewController, DoneCancelAction {

    var dateTimePicker: DateTimePicker!

    override func viewDidLoad() {
        super.viewDidLoad()
        dateTimePicker.delegate = self
    }

    func doneAction() {
        // your code
    }

    func cancelAction() {
        // your code
    }

}

A functional way to add Target in UIButton using closures, First we declare a closure with type alias, that takes the Sender(the button) as a parameter. Then we extend UIButton, adding a stored property and use� Note though that a closure doesn't need to be a self-invoking function, but it can be. When a closure is self invoking (i.e. immediately called by adding after the function), this means the return value is immediately calculated, rather than the function being returned and the return value being calculated later once the function is invoked. A


1.the right way to do this is using protocols just make a DateTypeProtocol add submit button and cancel button action functions to it.

2.make separate functions on your utility class and call the protocol functions using delegates

3.then while using it let the class using it conform the protocols and implement the actions there.

Simplifying communication patterns with closure in Swift, In iOS, we can handle button tap by using addTarget which is a function that any UIControl has: let button = UIButton() button.addTarget(self� For example: We want to create a button (using the Tk toolkit) giving it a subroutine reference. But it will be a problem if we want to give a subroutine to 2 different buttons on the screen. Therefore to do that, we’ll use a ‘smart’ callback routine i.e a closure.


Creating button action programatically using closure : swift, How can I add action to a button using a closure? Or do I have to create a function and pass the function's name as the selector? 2 comments. share. Notice that I use a normally open contact as input actuator, even for the stop button.This is because I’ve already used normally closed logic in the software.. When the input actuator is activated, the input bit will turn ON or 1.


ActionKit/ActionKit: Easy, closure-based Swift methods for , Easy, closure-based Swift methods for working with interactive UIKit elements. ActionKit is a light-weight, easy to use framework that wraps the target-action design touchUpInside) { (control: UIControl) in guard let button = control as? So, I decided to change it in the simplest way and do a quick and easy zipper to button closure pattern hack. NOTE: I am using this dress pattern for the tutorial, but you can apply this method to most dress or top sewing patterns that have a similar construction in the back.


Swift Lazy Initialization with Closures, In order to create a button in Swift, you probably have done something like this, First, let's demonstrate how to create an object using a closure. We will design� You searched for: unique buttons and closures! Etsy is the home to thousands of handmade, vintage, and one-of-a-kind products and gifts related to your search. No matter what you’re looking for or where you are in the world, our global marketplace of sellers can help you find unique and affordable options.