Skip to content

Instantly share code, notes, and snippets.

@beelsebob
Created November 2, 2011 17:40
Show Gist options
  • Save beelsebob/1334319 to your computer and use it in GitHub Desktop.
Save beelsebob/1334319 to your computer and use it in GitHub Desktop.
#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
#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