Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I perform the handler of a UIAlertAction?

I'm trying to write a helper class to allow our app to support both UIAlertAction and UIAlertView. However, when writing the alertView:clickedButtonAtIndex: method for the UIAlertViewDelegate, I came across this issue: I see no way to execute the code in the handler block of a UIAlertAction.

I'm trying to do this by keeping an array of UIAlertActions in a property called handlers

@property (nonatomic, strong) NSArray *handlers;

and then implement a delegate like such:

- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    UIAlertAction *action = self.handlers[buttonIndex];
    if (action.enabled)
        action.handler(action);
}

However, there is no action.handler property, or indeed any way I can see to fetch that, since the UIAlertAction header just has:

NS_CLASS_AVAILABLE_IOS(8_0) @interface UIAlertAction : NSObject <NSCopying>

+ (instancetype)actionWithTitle:(NSString *)title style:(UIAlertActionStyle)style handler:(void (^)(UIAlertAction *action))handler;

@property (nonatomic, readonly) NSString *title;
@property (nonatomic, readonly) UIAlertActionStyle style;
@property (nonatomic, getter=isEnabled) BOOL enabled;

@end

Is there some other way to execute the code in the handler block of a UIAlertAction?

like image 398
Ky. Avatar asked Apr 21 '15 13:04

Ky.


2 Answers

After some experimentation I just figured this out. Turns out that the handler block can be cast as a function pointer, and the function pointer can be executed.

Like so

//Get the UIAlertAction
UIAlertAction *action = self.handlers[buttonIndex];

//Cast the handler block into a form that we can execute
void (^someBlock)(id obj) = [action valueForKey:@"handler"];

//Execute the block
someBlock(action);
like image 111
Drew H Avatar answered Oct 18 '22 03:10

Drew H


Wrapper classes are great, eh?

In the .h:

@interface UIAlertActionWrapper : NSObject

@property (nonatomic, strong) void (^handler)(UIAlertAction *);
@property (nonatomic, strong) NSString *title;
@property (nonatomic, assign) UIAlertActionStyle style;
@property (nonatomic, assign) BOOL enabled;

- (id) initWithTitle: (NSString *)title style: (UIAlertActionStyle)style handler: (void (^)(UIAlertAction *))handler;

- (UIAlertAction *) toAlertAction;

@end

and in the .m:

- (UIAlertAction *) toAlertAction
{
    UIAlertAction *action = [UIAlertAction actionWithTitle:self.title style:self.style handler:self.handler];
    action.enabled = self.enabled;
    return action;
}

...

- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    UIAlertActionWrapper *action = self.helpers[buttonIndex];
    if (action.enabled)
        action.handler(action.toAlertAction);
}

All you have to do is make sure UIAlertActionWrappers are inserted into helpers instead of UIAlertActions.

This way, you can make all properties gettable and settable to your heart's content, and still retain the functionality provided by the original class.

like image 2
Ky. Avatar answered Oct 18 '22 05:10

Ky.