Skip to content

Commit

Permalink
Changed back to target/action. Added a selfReference to remove the ne…
Browse files Browse the repository at this point in the history
…ed to keep a reference in the calling class. Added customButton functionality - the option to add buttons to the UIToolbar that make the picker select specific values when tapped. Fixed warnings raised by static analysis. Updated sample to demonstrate new functions.
  • Loading branch information
TimCinel committed Nov 14, 2011
1 parent 7252531 commit 42ce011
Show file tree
Hide file tree
Showing 11 changed files with 297 additions and 81 deletions.
26 changes: 19 additions & 7 deletions ActionSheetPicker.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,29 +28,41 @@
#import <Foundation/Foundation.h>

@interface ActionSheetPicker : NSObject <UIPickerViewDelegate, UIPickerViewDataSource>


@property (nonatomic, retain) UIView *pickerView;
@property (nonatomic, readonly) CGSize viewSize;
@property (nonatomic, retain) NSMutableArray *customButtons; // ((NSString *title, id value),(NSString *title, id value), ... )
@property (nonatomic, assign) BOOL hideCancel;

/* Create and display an action sheet picker. The returned picker is autoreleased.
"origin" must not be empty. It can be either an originating container view or a UIBarButtonItem to use with a popover arrow.
"delegate" must not be empty. It should respond to "onSuccess" actions.
"target" must not be empty. It should respond to "onSuccess" actions.
"rows" is an array of strings to use for the picker's available selection choices.
"initialSelection" is used to establish the initially selected row;
*/
+ (id)showPickerWithTitle:(NSString *)title rows:(NSArray *)data initialSelection:(NSInteger)index delegate:(id)delegate onSuccess:(SEL)action origin:(id)origin;
+ (id)showPickerWithTitle:(NSString *)title rows:(NSArray *)data initialSelection:(NSInteger)index target:(id)target action:(SEL)action origin:(id)origin;

// Create an action sheet picker, but don't display until a subsequent call to "showActionPicker". Receiver must release the picker when ready. */
- (id)initWithTitle:(NSString *)title rows:(NSArray *)data initialSelection:(NSInteger)index target:(id)target action:(SEL)action origin:(id)origin;

/* Create an action sheet picker, but don't display until a subsequent call to "showActionPicker". Receiver must release the picker when ready. */
- (id)initWithTitle:(NSString *)title rows:(NSArray *)data initialSelection:(NSInteger)index delegate:(id)delegate onSuccess:(SEL)action origin:(id)origin;

- (void)showActionPicker;
// Adds custom buttons to the left of the UIToolbar that select specified values
- (void)addCustomButtonWithTitle:(NSString *)title value:(id)value;

// For subclasses. This is used to send a message to the delegate upon a successful selection and dismissal of the picker (i.e. not canceled).
- (void)notifyDelegate:(id)delegate didSucceedWithAction:(SEL)action origin:(id)origin;
// Present the ActionSheetPicker
- (void)showActionSheetPicker;

// For subclasses. This is used to send a message to the target upon a successful selection and dismissal of the picker (i.e. not canceled).
- (void)notifyTarget:(id)target didSucceedWithAction:(SEL)action origin:(id)origin;

// For subclasses. This returns a configured picker view. Subclasses should autorelease.
- (UIPickerView *)configuredPickerView;

- (void)actionPickerDone:(id)sender;
- (void)actionPickerCancel:(id)sender;

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

@end
119 changes: 89 additions & 30 deletions ActionSheetPicker.m
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,22 @@
//SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//


#import "ActionSheetPicker.h"

@interface ActionSheetPicker()

@property (nonatomic, retain) UIBarButtonItem *barButtonItem;
@property (nonatomic, retain) NSArray *data;
@property (nonatomic, retain) UIView *containerView;
@property (nonatomic, assign) NSInteger selectedIndex;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, assign) id delegate;
@property (nonatomic, assign) id target;
@property (nonatomic, assign) SEL action;
@property (nonatomic, retain) UIActionSheet *actionSheet;
@property (nonatomic, retain) UIPopoverController *popOverController;
- (ActionSheetPicker *)initWithDelegate:(id)delegate onSuccess:(SEL)action origin:(id)origin;
@property (nonatomic, retain) NSObject *selfReference;

- (ActionSheetPicker *)initWithTarget:(id)target action:(SEL)action origin:(id)origin;

- (void)presentPickerForView:(UIView *)aView;
- (void)configureAndPresentPopoverForView:(UIView *)aView;
Expand All @@ -52,6 +54,7 @@ - (id)storedOrigin;
- (UIBarButtonItem *)createToolbarLabelWithTitle:(NSString *)aTitle;
- (UIToolbar *)createPickerToolbarWithTitle:(NSString *)aTitle;
- (UIBarButtonItem *)createButtonWithType:(UIBarButtonSystemItem)type target:(id)target action:(SEL)buttonAction;

@end

@implementation ActionSheetPicker
Expand All @@ -61,40 +64,47 @@ @implementation ActionSheetPicker
@synthesize data = _data;
@synthesize selectedIndex = _selectedIndex;
@synthesize title = _title;
@synthesize delegate = _delegate;
@synthesize target = _target;
@synthesize action = _action;
@synthesize actionSheet = _actionSheet;
@synthesize popOverController = _popOverController;
@synthesize selfReference = _selfReference;

@synthesize pickerView = _pickerView;
@dynamic viewSize;
@synthesize customButtons = _customButtons;
@synthesize hideCancel = _hideCancel;

#pragma mark -
#pragma mark NSObject


+ (id)showPickerWithTitle:(NSString *)title rows:(NSArray *)data initialSelection:(NSInteger)index delegate:(id)delegate onSuccess:(SEL)action origin:(id)origin {
ActionSheetPicker *picker = [[[ActionSheetPicker alloc] initWithTitle:title rows:data initialSelection:index delegate:delegate onSuccess:action origin:origin] autorelease];
[picker showActionPicker];
+ (id)showPickerWithTitle:(NSString *)title rows:(NSArray *)data initialSelection:(NSInteger)index target:(id)target action:(SEL)action origin:(id)origin {
ActionSheetPicker *picker = [[[ActionSheetPicker alloc] initWithTitle:title rows:data initialSelection:index target:target action:action origin:origin] autorelease];
[picker showActionSheetPicker];
return picker;
}

- (ActionSheetPicker *)initWithDelegate:(id)delegate onSuccess:(SEL)action origin:(id)origin {
NSParameterAssert( (origin != NULL) && (delegate != NULL) );
- (ActionSheetPicker *)initWithTarget:(id)target action:(SEL)action origin:(id)origin {
NSParameterAssert( (origin != NULL) && (target != NULL) );
self = [super init];
if (self) {
self.delegate = delegate;
self.target = target;
self.action = action;
if ([origin isKindOfClass:[UIBarButtonItem class]])
self.barButtonItem = origin;
else if ([origin isKindOfClass:[UIView class]])
self.containerView = origin;

//allows us to use this without needing to store a reference in calling class
self.selfReference = self;
}
return self;
}

- (id)initWithTitle:(NSString *)title rows:(NSArray *)data initialSelection:(NSInteger)index delegate:(id)delegate onSuccess:(SEL)action origin:(id)origin {
NSParameterAssert( (origin != NULL) && (delegate != NULL) );
self = [self initWithDelegate:delegate onSuccess:action origin:origin];
- (id)initWithTitle:(NSString *)title rows:(NSArray *)data initialSelection:(NSInteger)index target:(id)target action:(SEL)action origin:(id)origin {
NSParameterAssert( (origin != NULL) && (target != NULL) );
self = [self initWithTarget:target action:action origin:origin];
if (self) {
self.data = data;
self.selectedIndex = index;
Expand All @@ -107,10 +117,11 @@ - (void)dealloc {
self.actionSheet = nil;
self.popOverController = nil;
self.data = nil;
self.customButtons = nil;
self.pickerView = nil;
self.containerView = nil;
self.barButtonItem = nil;
self.delegate = nil;
self.target = nil;
[super dealloc];
}

Expand All @@ -129,11 +140,23 @@ - (UIView *)configuredPickerView {
return stringPicker;
}

- (void)showActionPicker {

- (void)addCustomButtonWithTitle:(NSString *)title value:(id)value {
NSArray *customButtonArray = [[NSArray alloc] initWithObjects:title, value, nil];

if (nil == self.customButtons)
_customButtons = [[NSMutableArray alloc] init];

[self.customButtons addObject:customButtonArray];
[customButtonArray release];

}

- (void)showActionSheetPicker {
UIView *masterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.viewSize.width, 260)];
UIToolbar *pickerToolbar = [self createPickerToolbarWithTitle:self.title];
[pickerToolbar setBarStyle:UIBarStyleBlackTranslucent];
[masterView addSubview:pickerToolbar];
[pickerToolbar release];
self.pickerView = [self configuredPickerView];
NSAssert(_pickerView != NULL, @"Picker view failed to instantiate, perhaps you have invalid component data.");
[masterView addSubview:_pickerView];
Expand All @@ -142,11 +165,15 @@ - (void)showActionPicker {
}

// subclasses should override this for custom behavior
- (void)notifyDelegate:(id)delegate didSucceedWithAction:(SEL)action origin:(id)origin {
- (void)notifyTarget:(id)target didSucceedWithAction:(SEL)action origin:(id)origin {

if (!self.data)
return;
if ([delegate respondsToSelector:action])
[delegate performSelector:action withObject:[NSNumber numberWithInt:self.selectedIndex] withObject:origin];
if ([target respondsToSelector:action])
[target performSelector:action withObject:[NSNumber numberWithInt:self.selectedIndex] withObject:origin];
else
NSAssert(NO, @"Invalid target/action ( %s / %s ) combination used for ActionSheetPicker", object_getClassName(target), (char *)action);

}

#pragma mark - String Picker Data Source
Expand Down Expand Up @@ -227,9 +254,9 @@ - (void)presentPopover:(UIPopoverController *)popover {
#pragma mark - Convenient Utilities

- (void)actionPickerDone:(id)sender {
NSAssert(self.delegate != NULL, @"Cannot perform an action on a null target");
NSAssert(self.target != NULL, @"Cannot perform an action on a null target");
NSAssert(self.action != NULL, @"Cannot perform a null action");
[self notifyDelegate:self.delegate didSucceedWithAction:self.action origin:[self storedOrigin]];
[self notifyTarget:self.target didSucceedWithAction:self.action origin:[self storedOrigin]];
[self dismissPicker];
}

Expand All @@ -242,31 +269,63 @@ - (void)dismissPicker {
[_actionSheet dismissWithClickedButtonIndex:0 animated:YES];
else if (self.popOverController)
[_popOverController dismissPopoverAnimated:YES];
self.selfReference = nil;
}

- (void)customButtonPressed:(id)sender {
UIBarButtonItem *button = (UIBarButtonItem*)sender;
NSInteger index = button.tag;

NSAssert((0 <= index && index < self.customButtons.count), @"Bad custom button tag: %d, custom button count: %d", index, self.customButtons.count);
NSAssert([self.pickerView respondsToSelector:@selector(selectRow:inComponent:animated:)], @"customButtonPressed not overridden, cannot interact with subclassed pickerView");

//retrieve custom button's associated value index
NSInteger itemValue = [[[self.customButtons objectAtIndex:index] objectAtIndex:1] intValue];

UIPickerView *picker = (UIPickerView *)self.pickerView;

[picker selectRow:itemValue inComponent:0 animated:YES];
[self pickerView:picker didSelectRow:itemValue inComponent:0];
}

- (UIToolbar *)createPickerToolbarWithTitle:(NSString *)title {
CGRect frame = CGRectMake(0, 0, self.viewSize.width, 44);
UIToolbar *pickerToolbar = [[UIToolbar alloc] initWithFrame:frame];
UIToolbar *pickerToolbar = [[[UIToolbar alloc] initWithFrame:frame] autorelease];
pickerToolbar.barStyle = UIBarStyleBlackOpaque;
NSMutableArray *barItems = [[NSMutableArray alloc] init];

UIBarButtonItem *cancelBtn = [self createButtonWithType:UIBarButtonSystemItemCancel target:self action:@selector(actionPickerCancel:)];
[barItems addObject:cancelBtn];
[cancelBtn release];
/* custom buttons */
if (nil != self.customButtons) {
for (NSInteger i = 0; i < self.customButtons.count; i++) {
//create the button
UIBarButtonItem *customButton = [[[UIBarButtonItem alloc] initWithTitle:[[self.customButtons objectAtIndex:i] objectAtIndex:0]
style:UIBarButtonItemStyleBordered
target:self
action:@selector(customButtonPressed:)] autorelease];
//record associated customButtons index
customButton.tag = i;

//add to button list
[barItems addObject:customButton];
}
}

if (NO == self.hideCancel) {
UIBarButtonItem *cancelBtn = [self createButtonWithType:UIBarButtonSystemItemCancel target:self action:@selector(actionPickerCancel:)];
[barItems addObject:cancelBtn];
}

UIBarButtonItem *flexSpace = [self createButtonWithType:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
[barItems addObject:flexSpace];

if (title){
UIBarButtonItem *labelButton = [self createToolbarLabelWithTitle:title];
[barItems addObject:labelButton];
[labelButton release];
[barItems addObject:flexSpace];
}

UIBarButtonItem *doneButton = [self createButtonWithType:UIBarButtonSystemItemDone target:self action:@selector(actionPickerDone:)];
[barItems addObject:doneButton];
[doneButton release];
[flexSpace release];

[pickerToolbar setItems:barItems animated:YES];
[barItems release];
Expand All @@ -280,13 +339,13 @@ - (UIBarButtonItem *)createToolbarLabelWithTitle:(NSString *)aTitle {
[toolBarItemlabel setFont:[UIFont boldSystemFontOfSize:16]];
[toolBarItemlabel setBackgroundColor:[UIColor clearColor]];
toolBarItemlabel.text = aTitle;
UIBarButtonItem *buttonLabel =[[UIBarButtonItem alloc]initWithCustomView:toolBarItemlabel];
UIBarButtonItem *buttonLabel = [[[UIBarButtonItem alloc]initWithCustomView:toolBarItemlabel] autorelease];
[toolBarItemlabel release];
return buttonLabel;
}

- (UIBarButtonItem *)createButtonWithType:(UIBarButtonSystemItem)type target:(id)target action:(SEL)buttonAction {
return [[UIBarButtonItem alloc] initWithBarButtonSystemItem:type target:target action:buttonAction];
return [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:type target:target action:buttonAction] autorelease];
}

- (BOOL)isViewPortrait {
Expand Down
4 changes: 2 additions & 2 deletions Pickers/ActionSheetDatePicker.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@

+ (id)showPickerWithTitle:(NSString *)title
datePickerMode:(UIDatePickerMode)datePickerMode selectedDate:(NSDate *)selectedDate
delegate:(id)delegate onSuccess:(SEL)action origin:(id)origin;
target:(id)target action:(SEL)action origin:(id)origin;

- (id)initWithTitle:(NSString *)title
datePickerMode:(UIDatePickerMode)datePickerMode selectedDate:(NSDate *)selectedDate
delegate:(id)delegate onSuccess:(SEL)action origin:(id)origin;
target:(id)target action:(SEL)action origin:(id)origin;

- (void)eventForDatePicker:(id)sender;

Expand Down
36 changes: 28 additions & 8 deletions Pickers/ActionSheetDatePicker.m
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@

#import "ActionSheetDatePicker.h"

#import <objc/message.h>

@interface ActionSheetDatePicker()
@property (nonatomic, assign) UIDatePickerMode datePickerMode;
@property (nonatomic, retain) NSDate *selectedDate;
Expand All @@ -39,18 +41,18 @@ @implementation ActionSheetDatePicker

+ (id)showPickerWithTitle:(NSString *)title
datePickerMode:(UIDatePickerMode)datePickerMode selectedDate:(NSDate *)selectedDate
delegate:(id)delegate onSuccess:(SEL)action origin:(id)origin {
target:(id)target action:(SEL)action origin:(id)origin {
ActionSheetDatePicker *picker = [[[ActionSheetDatePicker alloc] initWithTitle:title
datePickerMode:datePickerMode selectedDate:selectedDate
delegate:delegate onSuccess:action origin:origin] autorelease];
[picker showActionPicker];
target:target action:action origin:origin] autorelease];
[picker showActionSheetPicker];
return picker;
}

- (id)initWithTitle:(NSString *)title
datePickerMode:(UIDatePickerMode)datePickerMode selectedDate:(NSDate *)selectedDate
delegate:(id)delegate onSuccess:(SEL)action origin:(id)origin {
self = [super initWithTitle:title rows:nil initialSelection:0 delegate:delegate onSuccess:action origin:origin];
target:(id)target action:(SEL)action origin:(id)origin {
self = [super initWithTitle:title rows:nil initialSelection:0 target:target action:action origin:origin];
if (self) {
self.datePickerMode = datePickerMode;
self.selectedDate = selectedDate;
Expand All @@ -72,9 +74,11 @@ - (UIView *)configuredPickerView {
return datePicker;
}

- (void)notifyDelegate:(id)delegate didSucceedWithAction:(SEL)action origin:(id)origin {
if ([delegate respondsToSelector:action])
[delegate performSelector:action withObject:self.selectedDate withObject:origin];
- (void)notifyTarget:(id)target didSucceedWithAction:(SEL)action origin:(id)origin {
if ([target respondsToSelector:action])
objc_msgSend(target, action, self.selectedDate, origin);
else
NSAssert(NO, @"Invalid target/action ( %s / %s ) combination used for ActionSheetPicker", object_getClassName(target), (char *)action);
}

- (void)eventForDatePicker:(id)sender {
Expand All @@ -84,4 +88,20 @@ - (void)eventForDatePicker:(id)sender {
}
}


- (void)customButtonPressed:(id)sender {
UIBarButtonItem *button = (UIBarButtonItem*)sender;
NSInteger index = button.tag;

NSAssert((0 <= index && index < self.customButtons.count), @"Bad custom button tag: %d, custom button count: %d", index, self.customButtons.count);
NSAssert([self.pickerView respondsToSelector:@selector(setDate:animated:)], @"Bad pickerView for ActionSheetDatePicker, doesn't respond to setDate:animated:");

//retrieve custom button's associated value index
NSDate *itemValue = [[self.customButtons objectAtIndex:index] objectAtIndex:1];
UIDatePicker *picker = (UIDatePicker *)self.pickerView;

[picker setDate:itemValue animated:YES];
[self eventForDatePicker:picker];
}

@end
4 changes: 2 additions & 2 deletions Pickers/ActionSheetDistancePicker.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@
+ (id)showPickerWithTitle:(NSString *)title
bigUnitString:(NSString *)bigUnitString bigUnitMax:(NSInteger)bigUnitMax selectedBigUnit:(NSInteger)selectedBigUnit
smallUnitString:(NSString*)smallUnitString smallUnitMax:(NSInteger)smallUnitMax selectedSmallUnit:(NSInteger)selectedSmallUnit
delegate:(id)delegate onSuccess:(SEL)action origin:(id)origin;
target:(id)target action:(SEL)action origin:(id)origin;

- (id)initWithTitle:(NSString *)title
bigUnitString:(NSString *)bigUnitString bigUnitMax:(NSInteger)bigUnitMax selectedBigUnit:(NSInteger)selectedBigUnit
smallUnitString:(NSString*)smallUnitString smallUnitMax:(NSInteger)smallUnitMax selectedSmallUnit:(NSInteger)selectedSmallUnit
delegate:(id)delegate onSuccess:(SEL)action origin:(id)origin;
target:(id)target action:(SEL)action origin:(id)origin;
@end
Loading

0 comments on commit 42ce011

Please sign in to comment.