Is it possible to pass an Objective-C block for the @selector
argument in a UIButton
? i.e., Is there any way to get the following to work?
[closeOverlayButton addTarget:self action:^ {[anotherIvarLocalToThisMethod removeFromSuperview];} forControlEvents:UIControlEventTouchUpInside];
Thanks
Yes, but you'd have to use a category.
Something like:
@interface UIControl (DDBlockActions) - (void) addEventHandler:(void(^)(void))handler forControlEvents:(UIControlEvents)controlEvents; @end
The implementation would be a bit trickier:
#import <objc/runtime.h> @interface DDBlockActionWrapper : NSObject @property (nonatomic, copy) void (^blockAction)(void); - (void) invokeBlock:(id)sender; @end @implementation DDBlockActionWrapper @synthesize blockAction; - (void) dealloc { [self setBlockAction:nil]; [super dealloc]; } - (void) invokeBlock:(id)sender { [self blockAction](); } @end @implementation UIControl (DDBlockActions) static const char * UIControlDDBlockActions = "unique"; - (void) addEventHandler:(void(^)(void))handler forControlEvents:(UIControlEvents)controlEvents { NSMutableArray * blockActions = objc_getAssociatedObject(self, &UIControlDDBlockActions); if (blockActions == nil) { blockActions = [NSMutableArray array]; objc_setAssociatedObject(self, &UIControlDDBlockActions, blockActions, OBJC_ASSOCIATION_RETAIN); } DDBlockActionWrapper * target = [[DDBlockActionWrapper alloc] init]; [target setBlockAction:handler]; [blockActions addObject:target]; [self addTarget:target action:@selector(invokeBlock:) forControlEvents:controlEvents]; [target release]; } @end
Some explanation:
DDBlockActionWrapper
. This is a simple class that has a block property (the block we want to get invoked), and a method that simply invokes that block.UIControl
category simply instantiates one of these wrappers, gives it the block to be invoked, and then tells itself to use that wrapper and its invokeBlock:
method as the target and action (as normal).UIControl
category uses an associated object to store an array of DDBlockActionWrappers
, because UIControl
does not retain its targets. This array is to ensure that the blocks exist when they're supposed to be invoked.DDBlockActionWrappers
get cleaned up when the object is destroyed, so we're doing a nasty hack of swizzling out -[UIControl dealloc]
with a new one that removes the associated object, and then invokes the original dealloc
code. Tricky, tricky.Finally, this code was typed in the browser and has not been compiled. There are probably some things wrong with it. Your mileage may vary.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With