UIButton block equivalent to addTarget:action:forControlEvents: method?

Viewed 37472

I looked around, but couldn't find this on the internet, nor anywhere in the Apple docs, so I'm guessing it doesn't exist.

But is there a iOS4 blocks equivalent API to:

[button addTarget:self action:@selector(tappy:) forControlEvents:UIControlEventTouchUpInside];

I suppose this could be implemented using a category, but would rather not write this myself due to extreme laziness :)

Something like this would be awesome:

[button handleControlEvent:UIControlEventTouchUpInside withBlock:^ { NSLog(@"I was tapped!"); }];
10 Answers

Swift 4

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 addAction(for controlEvents: UIControlEvents = .primaryActionTriggered, action: @escaping () -> ()) {
        let sleeve = ClosureSleeve(attachTo: self, closure: action)
        addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: controlEvents)
    }
}

Example Usage:

button.addAction {
    print("button pressed")
}

I created a library to do just this!

It supports UIControl (UIButton), UIBarButtonItem, and UIGestureRecognizer. It is also supported using CocoaPods.

https://github.com/lavoy/ALActionBlocks

// Assuming you have a UIButton named 'button'
[button handleControlEvents:UIControlEventTouchUpInside withBlock:^(id weakControl) {
    NSLog(@"button pressed");
}];

Install

pod 'ALActionBlocks'

A simpler solution (one without extensions, etc.):

void (^eventHandlerBlock)(void) = ^{
    printf("\nHandling event for button %lu\n", some_local_variable);
};
objc_setAssociatedObject(button, @selector(invoke), eventHandlerBlock, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[button addTarget:eventHandlerBlock action:@selector(invoke) forControlEvents:UIControlEventAllEvents];

A couple of things to keep in mind when substituting selectors for blocks:

  1. If you declare the block variable static, you can omit objc_setAssociatedObject; however, you cannot use any non-compile constants (such as some_local_variable).
  2. You can substitute void(^)(void) with dispatch_block_t.
Related