advantis
4/20/2012 - 1:40 PM

UIActionSheet subclass that brings simple block API

UIActionSheet subclass that brings simple block API

//
//  Copyright © 2011 Yuri Kotov
//


#import "ADVActionSheet.h"
#import <objc/runtime.h>

static inline BOOL ProtocolContainsSelector (Protocol *protocol, SEL selector)
{
    return NULL != protocol_getMethodDescription(protocol, selector, NO, YES).name;
}

@interface ADVActionSheet ()
@property (nonatomic, strong) NSMutableArray *actions;
@property (nonatomic, weak) id<UIActionSheetDelegate> trueDelegate;
@end

@implementation ADVActionSheet

#pragma mark - ADVActionSheet
- (NSInteger) addButtonWithTitle:(NSString *)title action:(ADVSheetAction)action
{
    NSInteger index = [super addButtonWithTitle:title];
    self.actions[index] = action ?: (ADVSheetAction)^{};
    return index;
}

/**
* UIActionSheet performs a series of "respondsToSelector" calls on delegate change
* to determine which of the optional protocol methods it implements and cache
* this info. Therefore we need to force it to update this info for a new delegate.
*/
- (void) refreshSupportedOptionalMethods
{
    super.delegate = nil;
    super.delegate = self;
}

#pragma mark - UIActionSheet
- (id<UIActionSheetDelegate>) delegate
{
    return self.trueDelegate;
}

- (void) setDelegate:(id<UIActionSheetDelegate>)delegate
{
    if (delegate != self.trueDelegate && delegate != self)
    {
        self.trueDelegate = delegate;
        [self refreshSupportedOptionalMethods];
    }
}

- (NSInteger) addButtonWithTitle:(NSString *)title
{
    return [self addButtonWithTitle:title action:nil];
}

#pragma mark - NSObject
- (id) init
{
    self = [super init];
    if (self)
    {
        _actions = [[NSMutableArray alloc] initWithCapacity:4];
        super.delegate = self;
    }
    return self;
}

- (BOOL) respondsToSelector:(SEL)selector
{
    BOOL responds = [super respondsToSelector:selector];
    if (!responds && ProtocolContainsSelector(@protocol(UIActionSheetDelegate), selector))
    {
        responds = [self.trueDelegate respondsToSelector:selector];
    }
    return responds;
}

- (id) forwardingTargetForSelector:(SEL)selector
{
    return ProtocolContainsSelector(@protocol(UIActionSheetDelegate), selector)
            ? self.trueDelegate
            : [super forwardingTargetForSelector:selector];
}

#pragma mark - UIActionSheetDelegate
- (void) actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if ([self.trueDelegate respondsToSelector:_cmd])
    {
        [self.trueDelegate actionSheet:actionSheet clickedButtonAtIndex:buttonIndex];
    }

    // Check for the case of tapping outside an action sheet on iPad
    // Kudos to @parfeon for pointing this out
    if (buttonIndex < [self.actions count])
    {
        ((ADVSheetAction)self.actions[buttonIndex])();
    }
}

@end
//
//  Copyright © 2011 Yuri Kotov
//


#import <UIKit/UIKit.h>

typedef void(^ADVSheetAction) ();

@interface ADVActionSheet : UIActionSheet <UIActionSheetDelegate>

- (NSInteger) addButtonWithTitle:(NSString *)title action:(ADVSheetAction)action;

@end