Search code examples
iosuicontrolios14uiaction

Why is the new iOS 14 UIControl action syntax so terrible?


New in iOS 14, we can attach an action handler directly to a UIControl:

    let action = UIAction(title:"") { action in
        print("howdy!")
    }
    button.addAction(action, for: .touchUpInside)

That's cool in its way, but the syntax is infuriating. I have to form the UIAction first. I have to give the UIAction a title, even though that title will never appear in the interface. Isn't there a better way?


Solution

  • First, you don't need to supply the title. This is (now) legal:

        let action = UIAction { action in
            print("howdy!")
        }
        button.addAction(action, for: .touchUpInside)
    

    Second, you don't really need the separate line to define the action, so you can say this:

        button.addAction(.init { action in
            print("howdy!")
        }, for: .touchUpInside)
    

    However, that's still infuriating, because now I've got a closure in the middle of the addAction call. It ought to be a trailing closure! The obvious solution is an extension:

    extension UIControl {
        func addAction(for event: UIControl.Event, handler: @escaping UIActionHandler) {
            self.addAction(UIAction(handler:handler), for:event)
        }
    }
    

    Problem solved! Now I can talk the way I should have been permitted to all along:

        button.addAction(for: .touchUpInside) { action in
            print("howdy!")
        }
    

    [Extra info: Where's the sender in this story? It's inside the action. UIAction has a sender property. So in that code, action.sender is the UIButton.]