Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added callback for custom button being pressed #81

Merged
merged 21 commits into from
Oct 31, 2014
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions ObjC-Example/Example/Classes/ActionSheetPickerViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,17 @@ - (IBAction)selectALocale:(UIControl *)sender {
NSLog(@"Locale Picker Canceled");
};
ActionSheetLocalePicker *picker = [[ActionSheetLocalePicker alloc] initWithTitle:@"Select Locale:" initialSelection:[[NSTimeZone alloc] initWithName:@"Antarctica/McMurdo"] doneBlock:done cancelBlock:cancel origin:sender];

[picker addCustomButtonWithTitle:@"My locale" value:[NSTimeZone localTimeZone]];
__weak ActionSheetLocalePicker *weakPicker = picker;
__weak UIControl *weakSender = sender;
[picker addCustomButtonWithTitle:@"Hide" actionBlock:^{
if ([weakSender respondsToSelector:@selector(setText:)]) {
[weakSender performSelector:@selector(setText:) withObject:[NSTimeZone localTimeZone].name];
}
[weakPicker hidePickerWithCancelAction];
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO: When people want to set custom action for button - explicit behaviour to hide picker automatically after button pressed. And it also simplify usage of picker custom buttons.
So, it's better to move hidePickerWithCancelAction inside of Picker logic and just dismiss picker and not call cancel action in this case.
Moreover, it's preferrable to call actionBlock only after picker already dismissed to avoid bugs, such #63 (when you want to show another picker immediately after first one is dismissed.

I'm ready to discuss it in details.

}];

picker.hideCancel = YES;
[picker showActionSheetPicker];
}
Expand Down
30 changes: 28 additions & 2 deletions Pickers/AbstractActionSheetPicker.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,30 @@
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

#define SuppressPerformSelectorLeakWarning(Stuff) \
do { \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \
Stuff; \
_Pragma("clang diagnostic pop") \
} while (0)

static NSString *const kButtonValue = @"buttonValue";
typedef NS_ENUM(NSInteger, ActionType)
{
Value,
Selector,
Block
};

static NSString *const kButtonTitle = @"buttonTitle";
typedef void (^ActionBlock)(void);

static NSString *const kButtonValue = @"buttonValue";

static NSString *const kButtonTitle = @"buttonTitle";

static NSString *const kActionType = @"buttonAction";

static NSString *const kActionTarget = @"buttonActionTarget";

@interface AbstractActionSheetPicker : NSObject<UIPopoverControllerDelegate>
@property (nonatomic, strong) UIToolbar* toolbar;
Expand Down Expand Up @@ -63,6 +83,12 @@ static NSString *const kButtonTitle = @"buttonTitle";
// Adds custom buttons to the left of the UIToolbar that select specified values
- (void)addCustomButtonWithTitle:(NSString *)title value:(id)value;

// Adds custom buttons to the left of the UIToolbar that implement specified block
- (void)addCustomButtonWithTitle:(NSString *)title actionBlock:(ActionBlock)block;

// Adds custom buttons to the left of the UIToolbar that implement specified selector
- (void)addCustomButtonWithTitle:(NSString*)title target:(id)target selector:(SEL)selector;

//For subclasses. This responds to a custom button being pressed.
- (IBAction)customButtonPressed:(id)sender;

Expand Down
92 changes: 81 additions & 11 deletions Pickers/AbstractActionSheetPicker.m
Original file line number Diff line number Diff line change
Expand Up @@ -230,38 +230,108 @@ - (void)dismissPicker

#pragma mark - Custom Buttons

- (void)addCustomButtonWithTitle:(NSString *)title value:(id)value
- (NSMutableArray *)customButtons
{
if ( !self.customButtons )
if (!_customButtons) {
_customButtons = [[NSMutableArray alloc] init];
}

return _customButtons;
}

- (void)addCustomButtonWithTitle:(NSString *)title value:(id)value
{
if ( !title )
title = @"";
if ( !value )
value = @0;
NSDictionary *buttonDetails = @{
kButtonTitle : title,
kActionType : @(Value),
kButtonValue : value
};
[self.customButtons addObject:buttonDetails];
}

- (void)addCustomButtonWithTitle:(NSString *)title actionBlock:(ActionBlock)block
{
if (!title) {
title = @"";
}
if (!block) {
block = (^{});
}
NSDictionary *buttonDetails = @{
kButtonTitle : title,
kActionType : @(Block),
kButtonValue : [block copy]
};
[self.customButtons addObject:buttonDetails];
}

- (void)addCustomButtonWithTitle:(NSString *)title target:(id)target selector:(SEL)selector
{
if (!title) {
title = @"";
}
if (!target) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The brackets here (in one-line expressions) is redundant. (not a mistake, just about code-style)

target = [NSNull null];
}
NSDictionary *buttonDetails = @{
kButtonTitle : title,
kActionType : @(Selector),
kActionTarget: target,
kButtonValue : [NSValue valueWithPointer:selector]
};
[self.customButtons addObject:buttonDetails];
}

- (IBAction)customButtonPressed:(id)sender
{
UIBarButtonItem *button = (UIBarButtonItem *) sender;
NSInteger index = button.tag;
NSAssert((index >= 0 && index < self.customButtons.count), @"Bad custom button tag: %ld, custom button count: %lu", (long)index, (unsigned long)self.customButtons.count);
NSAssert([self.pickerView respondsToSelector:@
selector(selectRow:inComponent:animated:)], @"customButtonPressed not overridden, cannot interact with subclassed pickerView");
selector(selectRow:inComponent:animated:)], @"customButtonPressed not overridden, cannot interact with subclassed pickerView");

NSDictionary *buttonDetails = (self.customButtons)[(NSUInteger) index];
NSAssert(buttonDetails != NULL, @"Custom button dictionary is invalid");
NSInteger buttonValue = [buttonDetails[kButtonValue] intValue];
UIPickerView *picker = (UIPickerView *) self.pickerView;
NSAssert(picker != NULL, @"PickerView is invalid");
[picker selectRow:buttonValue inComponent:0 animated:YES];
if ( [self respondsToSelector:@selector(pickerView:didSelectRow:inComponent:)] )
{
void (*objc_msgSendTyped)(id target, SEL _cmd, id pickerView, NSInteger row, NSInteger component) = (void *) objc_msgSend; // sending Integers as params
objc_msgSendTyped(self, @selector(pickerView:didSelectRow:inComponent:), picker, buttonValue, 0);

NSInteger actionType = [buttonDetails[kActionType] intValue];
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify, what is actionType - it should be type of ActionType, not NSInteger

switch (actionType) {
case Value: {
NSInteger buttonValue = [buttonDetails[kButtonValue] intValue];
UIPickerView *picker = (UIPickerView *) self.pickerView;
NSAssert(picker != NULL, @"PickerView is invalid");
[picker selectRow:buttonValue inComponent:0 animated:YES];
if ( [self respondsToSelector:@selector(pickerView:didSelectRow:inComponent:)] )
{
void (*objc_msgSendTyped)(id target, SEL _cmd, id pickerView, NSInteger row, NSInteger component) = (void *) objc_msgSend; // sending Integers as params
objc_msgSendTyped(self, @selector(pickerView:didSelectRow:inComponent:), picker, buttonValue, 0);
}
break;
}

case Block: {
ActionBlock actionBlock = buttonDetails[kButtonValue];
if (actionBlock) { actionBlock(); }
break;
}

case Selector: {
SEL selector = [buttonDetails[kButtonValue] pointerValue];
id target = buttonDetails[kActionTarget];
if (target && [target respondsToSelector:selector]) {
SuppressPerformSelectorLeakWarning (
[target performSelector:selector];
);
}
break;
}

default:
NSAssert(false, @"Unknown action type");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

break;
}
}

Expand Down
38 changes: 34 additions & 4 deletions Pickers/ActionSheetDatePicker.m
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,41 @@ - (void)customButtonPressed:(id)sender {
NSInteger index = button.tag;
NSAssert((index >= 0 && index < self.customButtons.count), @"Bad custom button tag: %zd, custom button count: %zd", index, self.customButtons.count);
NSAssert([self.pickerView respondsToSelector:@selector(setDate:animated:)], @"Bad pickerView for ActionSheetDatePicker, doesn't respond to setDate:animated:");

NSDictionary *buttonDetails = (self.customButtons)[(NSUInteger) index];
NSDate *itemValue = buttonDetails[kButtonValue];
UIDatePicker *picker = (UIDatePicker *)self.pickerView;
[picker setDate:itemValue animated:YES];
[self eventForDatePicker:picker];
NSAssert(buttonDetails != NULL, @"Custom button dictionary is invalid");

NSInteger actionType = [buttonDetails[kActionType] intValue];
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again: cast to ActionType

switch (actionType) {
case Value: {
NSDate *itemValue = buttonDetails[kButtonValue];
UIDatePicker *picker = (UIDatePicker *)self.pickerView;
[picker setDate:itemValue animated:YES];
[self eventForDatePicker:picker];
break;
}

case Block: {
ActionBlock actionBlock = buttonDetails[kButtonValue];
if (actionBlock) { actionBlock(); }
break;
}

case Selector: {
SEL selector = [buttonDetails[kButtonValue] pointerValue];
id target = buttonDetails[kActionTarget];
if (target && [target respondsToSelector:selector]) {
SuppressPerformSelectorLeakWarning (
[target performSelector:selector];
);
}
break;
}

default:
NSAssert(false, @"Unknown action type");
break;
}
}

@end
44 changes: 37 additions & 7 deletions Pickers/ActionSheetLocalePicker.m
Original file line number Diff line number Diff line change
Expand Up @@ -367,14 +367,44 @@ - (void)customButtonPressed:(id)sender {
UIBarButtonItem *button = (UIBarButtonItem*)sender;
NSInteger index = button.tag;
NSAssert((index >= 0 && index < self.customButtons.count), @"Bad custom button tag: %ld, custom button count: %lu", (long)index, (unsigned long)self.customButtons.count);

NSDictionary *buttonDetails = (self.customButtons)[(NSUInteger) index];
id itemValue = buttonDetails[kButtonValue];
if ( [itemValue isKindOfClass:[NSTimeZone class]] )
{
NSTimeZone *timeZone = (NSTimeZone *) itemValue;
self.initialTimeZone = timeZone;
[self setSelectedRows];
[self selectCurrentLocale:(UIPickerView *) self.pickerView];
NSAssert(buttonDetails != NULL, @"Custom button dictionary is invalid");

NSInteger actionType = [buttonDetails[kActionType] intValue];
switch (actionType) {
case Value: {
id itemValue = buttonDetails[kButtonValue];
if ( [itemValue isKindOfClass:[NSTimeZone class]] )
{
NSTimeZone *timeZone = (NSTimeZone *) itemValue;
self.initialTimeZone = timeZone;
[self setSelectedRows];
[self selectCurrentLocale:(UIPickerView *) self.pickerView];
}
break;
}

case Block: {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try to avoid duplication of code as much as possible.
In cases Block and Selector you duplicate code 3 times.
In every common cases in subclasses - just call method of super instead.

ActionBlock actionBlock = buttonDetails[kButtonValue];
if (actionBlock) { actionBlock(); }
break;
}

case Selector: {
SEL selector = [buttonDetails[kButtonValue] pointerValue];
id target = buttonDetails[kActionTarget];
if (target && [target respondsToSelector:selector]) {
SuppressPerformSelectorLeakWarning (
[target performSelector:selector];
);
}
break;
}

default:
NSAssert(false, @"Unknown action type");
break;
}
}

Expand Down