Created
November 2, 2011 17:40
-
-
Save beelsebob/1334319 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <UIKit/UIKit.h> | |
@class BDSegmentedControl; | |
@protocol BDSegmentedControlDelegate <NSObject> | |
- (NSUInteger)segmentedControl:(BDSegmentedControl *)segmentedControl numberOfMenuItemsForSegmentAtIndex:(NSUInteger)segmentIndex; | |
- (UIImage *)segmentedControl:(BDSegmentedControl *)segmentedControl imageForMenuBackgroundForSegmentAtIndex:(NSUInteger)segmentIndex; | |
- (UIImage *)segmentedControl:(BDSegmentedControl *)segmentedControl imageForMenuItemAtIndex:(NSUInteger)menuItemIndex forSegmentAtIndex:(NSUInteger)segmentIndex; | |
- (UIImage *)segmentedControl:(BDSegmentedControl *)segmentedControl imageForSelectedMenuItemAtIndex:(NSUInteger)menuItemIndex forSegmentAtIndex:(NSUInteger)segmentIndex; | |
- (UIImage *)segmentedControl:(BDSegmentedControl *)segmentedControl imageForSelectedSegment:(NSUInteger)segmentIndex whenMenuItemAtIndexIsSelected:(NSUInteger)menuItemIndex; | |
@end | |
@interface BDSegmentedControl : UIControl | |
- (id)initWithSegments:(NSUInteger)numberOfSegments; | |
@property (nonatomic, readwrite, assign) id<BDSegmentedControlDelegate> delegate; | |
@property (nonatomic, readwrite, assign) NSUInteger numberOfSegments; | |
@property (nonatomic, readwrite, assign) NSUInteger selectedSegmentIndex; | |
@property (nonatomic, readwrite, assign) NSUInteger selectedMenuItem; | |
@property (nonatomic, readwrite, assign) CGFloat imageScale; | |
- (void)setImage:(UIImage *)image forSegmentAtIndex:(NSUInteger)segment; | |
- (void)setSelectedImage:(UIImage *)image forSegmentAtIndex:(NSUInteger)segment; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "BDSegmentedControl.h" | |
@interface BDSegmentedControl () | |
@property (nonatomic, readwrite, retain) NSArray *segmentButtons; | |
@property (nonatomic, readwrite, retain) NSArray *menuItems; | |
@property (nonatomic, readwrite, retain) NSMutableArray *selectedImages; | |
@property (nonatomic, readwrite, assign) BOOL menuOpen; | |
@property (nonatomic, readwrite, retain) UIView *menuView; | |
@property (nonatomic, readwrite, assign) CGRect normalFrame; | |
- (void)segmentTapped:(id)sender; | |
- (void)menuItemTapped:(id)sender; | |
- (void)openMenu; | |
- (void)closeMenuThen:(void(^)(void))completion; | |
@end | |
@implementation BDSegmentedControl | |
{ | |
NSUInteger numberOfSegments; | |
NSUInteger selectedSegmentIndex; | |
NSUInteger selectedMenuItem; | |
} | |
@synthesize delegate; | |
@synthesize segmentButtons; | |
@synthesize menuItems; | |
@synthesize selectedImages; | |
@synthesize imageScale; | |
@synthesize menuOpen; | |
@synthesize menuView; | |
@synthesize normalFrame; | |
- (id)initWithSegments:(NSUInteger)initNumberOfSegments | |
{ | |
self = [super init]; | |
if (nil != self) | |
{ | |
[self setSelectedMenuItem:NSNotFound]; | |
[self setSegmentButtons:[NSArray array]]; | |
[self setSelectedImages:[NSMutableArray array]]; | |
[self setImageScale:1.0]; | |
[self setNumberOfSegments:initNumberOfSegments]; | |
[self setBackgroundColor:[UIColor clearColor]]; | |
[self setNormalFrame:[self frame]]; | |
} | |
return self; | |
} | |
- (id)initWithCoder:(NSCoder *)aDecoder | |
{ | |
self = [super initWithCoder:aDecoder]; | |
if (nil != self) | |
{ | |
[self setSelectedMenuItem:NSNotFound]; | |
[self setSegmentButtons:[NSArray array]]; | |
[self setSelectedImages:[NSMutableArray array]]; | |
[self setImageScale:1.0]; | |
[self setNumberOfSegments:1]; | |
[self setBackgroundColor:[UIColor clearColor]]; | |
[self setNormalFrame:[self frame]]; | |
} | |
return self; | |
} | |
- (id)init | |
{ | |
return [self initWithSegments:1]; | |
} | |
- (void)dealloc | |
{ | |
[self setSegmentButtons:nil]; | |
[self setMenuItems:nil]; | |
[self setSelectedImages:nil]; | |
[self setMenuView:nil]; | |
[super dealloc]; | |
} | |
- (NSUInteger)numberOfSegments | |
{ | |
return numberOfSegments; | |
} | |
- (void)setNumberOfSegments:(NSUInteger)newNumberOfSegments | |
{ | |
if (numberOfSegments != newNumberOfSegments) | |
{ | |
if (newNumberOfSegments < numberOfSegments) | |
{ | |
[self setSegmentButtons:[[self segmentButtons] subarrayWithRange:NSMakeRange(0, newNumberOfSegments)]]; | |
[self setSelectedImages:[[[[self selectedImages] subarrayWithRange:NSMakeRange(0, newNumberOfSegments)] mutableCopy] autorelease]]; | |
} | |
else | |
{ | |
NSMutableArray *newSegmentButtons = [[[self segmentButtons] mutableCopy] autorelease]; | |
for (NSUInteger i = numberOfSegments; i < newNumberOfSegments; i++) | |
{ | |
UIButton *b = [UIButton buttonWithType:UIButtonTypeCustom]; | |
[b setAdjustsImageWhenDisabled:NO]; | |
[b setAdjustsImageWhenHighlighted:NO]; | |
[b setShowsTouchWhenHighlighted:NO]; | |
[b addTarget:self action:@selector(segmentTapped:) forControlEvents:UIControlEventTouchUpInside]; | |
[b setSelected:i == [self selectedSegmentIndex]]; | |
[newSegmentButtons addObject:b]; | |
[self addSubview:b]; | |
[[self selectedImages] addObject:[NSNull null]]; | |
} | |
[self setSegmentButtons:newSegmentButtons]; | |
} | |
numberOfSegments = newNumberOfSegments; | |
[self setNeedsLayout]; | |
} | |
} | |
- (NSUInteger)selectedSegmentIndex | |
{ | |
return selectedSegmentIndex; | |
} | |
- (void)setSelectedSegmentIndex:(NSUInteger)newSelectedSegmentIndex | |
{ | |
if (selectedSegmentIndex != newSelectedSegmentIndex) | |
{ | |
id img = [[self selectedImages] objectAtIndex:selectedSegmentIndex]; | |
if ([img isKindOfClass:[UIImage class]]) | |
{ | |
[[[self segmentButtons] objectAtIndex:selectedSegmentIndex] setImage:img forState:UIControlStateSelected]; | |
} | |
[[[self segmentButtons] objectAtIndex:selectedSegmentIndex] setSelected:NO]; | |
[[[self segmentButtons] objectAtIndex:newSelectedSegmentIndex] setSelected:YES]; | |
selectedSegmentIndex = newSelectedSegmentIndex; | |
[self setSelectedMenuItem:NSNotFound]; | |
} | |
} | |
- (NSUInteger)selectedMenuItem | |
{ | |
return selectedMenuItem; | |
} | |
- (void)setSelectedMenuItem:(NSUInteger)newSelectedMenuItem | |
{ | |
if (selectedMenuItem != newSelectedMenuItem) | |
{ | |
if (NSNotFound == newSelectedMenuItem) | |
{ | |
[[[self segmentButtons] objectAtIndex:[self selectedSegmentIndex]] setImage:[[self selectedImages] objectAtIndex:[self selectedSegmentIndex]] forState:UIControlStateSelected]; | |
} | |
else | |
{ | |
[[[self segmentButtons] objectAtIndex:[self selectedSegmentIndex]] setImage:[[self delegate] segmentedControl:self imageForSelectedSegment:[self selectedSegmentIndex] whenMenuItemAtIndexIsSelected:newSelectedMenuItem] | |
forState:UIControlStateSelected]; | |
} | |
selectedMenuItem = newSelectedMenuItem; | |
} | |
} | |
- (void)segmentTapped:(id)sender | |
{ | |
NSUInteger newSelectedSegmentIndex = [[self segmentButtons] indexOfObject:sender]; | |
if (newSelectedSegmentIndex != [self selectedSegmentIndex]) | |
{ | |
[self closeMenuThen:^() | |
{ | |
[self setSelectedSegmentIndex:newSelectedSegmentIndex]; | |
if (0 != [[self delegate] segmentedControl:self numberOfMenuItemsForSegmentAtIndex:newSelectedSegmentIndex]) | |
{ | |
[self openMenu]; | |
} | |
else | |
{ | |
[self sendActionsForControlEvents:UIControlEventValueChanged]; | |
} | |
}]; | |
} | |
else if (![self menuOpen]) | |
{ | |
if (0 != [[self delegate] segmentedControl:self numberOfMenuItemsForSegmentAtIndex:newSelectedSegmentIndex]) | |
{ | |
[self openMenu]; | |
} | |
} | |
} | |
- (void)menuItemTapped:(id)sender | |
{ | |
NSUInteger newSelectedMenuItemIndex = [[self menuItems] indexOfObject:sender]; | |
if (newSelectedMenuItemIndex != [self selectedMenuItem]) | |
{ | |
[self closeMenuThen:^() | |
{ | |
[self setSelectedMenuItem:newSelectedMenuItemIndex]; | |
[self sendActionsForControlEvents:UIControlEventValueChanged]; | |
}]; | |
} | |
} | |
#define kButtonFadeLength 0.1 | |
- (void)openMenu | |
{ | |
[self setNormalFrame:[self frame]]; | |
UIButton *b = [[self segmentButtons] objectAtIndex:[self selectedSegmentIndex]]; | |
CGRect bFrame = [b frame]; | |
[self setMenuView:[[[UIView alloc] initWithFrame:CGRectMake(bFrame.origin.x, bFrame.origin.y + bFrame.size.height - 5.0f, bFrame.size.width, 0.0f)] autorelease]]; | |
[[self menuView] setOpaque:NO]; | |
[[self menuView] setBackgroundColor:[UIColor clearColor]]; | |
[self insertSubview:[self menuView] belowSubview:b]; | |
NSUInteger numberOfItems = [[self delegate] segmentedControl:self numberOfMenuItemsForSegmentAtIndex:[self selectedSegmentIndex]]; | |
CGFloat buttonFadePeriod = 0.3 - kButtonFadeLength; | |
CGFloat buttonFadeOffset = buttonFadePeriod / (numberOfItems + 1); | |
CGFloat buttonFadeStart = buttonFadeOffset; | |
CGFloat buttonTop = 0.0f; | |
CGFloat buttonWidth = [b bounds].size.width; | |
NSMutableArray *newMenuItems = [NSMutableArray arrayWithCapacity:numberOfItems]; | |
for (NSUInteger i = 0; i < numberOfItems; i++) | |
{ | |
UIImage *buttonImage = [[self delegate] segmentedControl:self imageForMenuItemAtIndex:i forSegmentAtIndex:[self selectedSegmentIndex]]; | |
CGFloat buttonHeight = [buttonImage size].height; | |
UIButton *menuItem = [[[UIButton alloc] initWithFrame:CGRectMake(0.0, buttonTop, buttonWidth, buttonHeight)] autorelease]; | |
[menuItem setImage:buttonImage forState:UIControlStateNormal]; | |
[menuItem setImage:[[self delegate] segmentedControl:self imageForSelectedMenuItemAtIndex:i forSegmentAtIndex:[self selectedSegmentIndex]] forState:UIControlStateSelected]; | |
[menuItem addTarget:self action:@selector(menuItemTapped:) forControlEvents:UIControlEventTouchUpInside]; | |
[menuItem setOpaque:NO]; | |
[menuItem setAlpha:0.0]; | |
[[self menuView] addSubview:menuItem]; | |
[newMenuItems addObject:menuItem]; | |
[UIView animateWithDuration:0.3 | |
delay:buttonFadeStart | |
options:UIViewAnimationCurveLinear | |
animations:^() | |
{ | |
[menuItem setAlpha:1.0]; | |
} | |
completion:^(BOOL finished) {}]; | |
buttonFadeStart += buttonFadeOffset; | |
buttonTop += buttonHeight; | |
} | |
[self setMenuItems:newMenuItems]; | |
[UIView animateWithDuration:0.3 | |
animations:^() | |
{ | |
[[self menuView] setFrame:CGRectMake(bFrame.origin.x, bFrame.origin.y + bFrame.size.height - 5.0, bFrame.size.width, buttonTop)]; | |
}]; | |
[self setMenuOpen:YES]; | |
[self setFrame:CGRectMake([self frame].origin.x, [self frame].origin.y, [self frame].size.width, [self frame].size.height + buttonTop)]; | |
} | |
- (void)closeMenuThen:(void(^)(void))completion | |
{ | |
[self setFrame:[self normalFrame]]; | |
UIButton *b = [[self segmentButtons] objectAtIndex:[self selectedSegmentIndex]]; | |
CGRect bFrame = [b frame]; | |
NSUInteger numberOfItems = [[self delegate] segmentedControl:self numberOfMenuItemsForSegmentAtIndex:[self selectedSegmentIndex]]; | |
CGFloat buttonFadePeriod = 0.3 - kButtonFadeLength; | |
CGFloat buttonFadeOffset = buttonFadePeriod / (numberOfItems + 1); | |
CGFloat buttonFadeStart = buttonFadeOffset; | |
for (NSInteger i = numberOfItems - 1; i >= 0; i--) | |
{ | |
[UIView animateWithDuration:0.3 | |
delay:buttonFadeStart | |
options:UIViewAnimationCurveLinear | |
animations:^() | |
{ | |
[[[self menuItems] objectAtIndex:i] setAlpha:0.0]; | |
} | |
completion:^(BOOL finished) {}]; | |
buttonFadeStart += buttonFadeOffset; | |
} | |
[UIView animateWithDuration:0.3 | |
animations:^() | |
{ | |
[[self menuView] setFrame:CGRectMake(bFrame.origin.x, bFrame.origin.y + bFrame.size.height - 5.0, bFrame.size.width, 0.0)]; | |
} | |
completion:^ (BOOL finished) | |
{ | |
[self setMenuItems:nil]; | |
[[self menuView] removeFromSuperview]; | |
[self setMenuView:nil]; | |
[self setMenuOpen:NO]; | |
completion(); | |
}]; | |
} | |
- (void)setImage:(UIImage *)image forSegmentAtIndex:(NSUInteger)segment | |
{ | |
UIButton *b = [[self segmentButtons] objectAtIndex:segment]; | |
[b setImage:image forState:UIControlStateNormal]; | |
[self setNeedsLayout]; | |
} | |
- (void)setSelectedImage:(UIImage *)image forSegmentAtIndex:(NSUInteger)segment | |
{ | |
UIButton *b = [[self segmentButtons] objectAtIndex:segment]; | |
[b setImage:image forState:UIControlStateSelected]; | |
[[self selectedImages] replaceObjectAtIndex:segment withObject:image]; | |
[self setNeedsLayout]; | |
} | |
- (void)layoutSubviews | |
{ | |
CGFloat o = [self bounds].origin.x; | |
CGFloat y = [self bounds].origin.y; | |
for (UIButton *button in [self segmentButtons]) | |
{ | |
UIImage *buttonImage = [button imageForState:UIControlStateNormal]; | |
CGFloat bw = [buttonImage size].width / [self imageScale]; | |
[button setFrame:CGRectMake(o, y, bw, [buttonImage size].height / [self imageScale])]; | |
o += bw; | |
} | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment