diff --git a/Examples/Sample.xcodeproj/project.pbxproj b/Examples/Sample.xcodeproj/project.pbxproj index 1f7b83d..7123d3a 100644 --- a/Examples/Sample.xcodeproj/project.pbxproj +++ b/Examples/Sample.xcodeproj/project.pbxproj @@ -15,7 +15,6 @@ 4FB285CA1A766D0300B573DE /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 4FB285C91A766D0300B573DE /* Default-568h@2x.png */; }; 4FB285D01A7671A400B573DE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FB285611A75E94C00B573DE /* AppDelegate.m */; }; 4FB285D11A7671A400B573DE /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FB285C31A766CEC00B573DE /* main.m */; }; - 4FB285D21A7671A400B573DE /* TableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FB285861A75E9F700B573DE /* TableViewController.m */; }; 4FB285D41A7671A400B573DE /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 336F7DD8CF855E383EFF085A /* libPods.a */; }; 4FB285D71A7671A400B573DE /* Storyboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4FB285C41A766CEC00B573DE /* Storyboard.storyboard */; }; 4FB285D81A7671A400B573DE /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 4FB285C91A766D0300B573DE /* Default-568h@2x.png */; }; @@ -34,8 +33,8 @@ 4FEA1D801B7A5DF800E1DBDF /* CollectionView-Example.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4FEA1D7F1B7A5DF800E1DBDF /* CollectionView-Example.storyboard */; }; 4FEA1D861B7A67E700E1DBDF /* UICollectionView+SupplementaryElementScrolling.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FEA1D851B7A67E700E1DBDF /* UICollectionView+SupplementaryElementScrolling.m */; }; 4FEFACCB1B7989C700D905B8 /* images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4FEFACCA1B7989C700D905B8 /* images.xcassets */; }; - 4FEFACCC1B798CA800D905B8 /* TableView-Example.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4FB285841A75E9F700B573DE /* TableView-Example.storyboard */; }; C46F97D4245BDD7548E1F345 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 336F7DD8CF855E383EFF085A /* libPods.a */; }; + F59D40EC1C6AE665004E9AAC /* TableView-Example.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4FB285841A75E9F700B573DE /* TableView-Example.storyboard */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -332,7 +331,7 @@ 4FEFACCB1B7989C700D905B8 /* images.xcassets in Resources */, 4FB285C71A766CEC00B573DE /* Storyboard.storyboard in Resources */, 4FB285CA1A766D0300B573DE /* Default-568h@2x.png in Resources */, - 4FEFACCC1B798CA800D905B8 /* TableView-Example.storyboard in Resources */, + F59D40EC1C6AE665004E9AAC /* TableView-Example.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -470,7 +469,6 @@ files = ( 4FB285D01A7671A400B573DE /* AppDelegate.m in Sources */, 4FB285D11A7671A400B573DE /* main.m in Sources */, - 4FB285D21A7671A400B573DE /* TableViewController.m in Sources */, 4FB285E11A76730300B573DE /* ScrollViewController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Examples/Sample/ScrollView Example/ScrollView-Example.storyboard b/Examples/Sample/ScrollView Example/ScrollView-Example.storyboard index 26789e4..8ab9741 100644 --- a/Examples/Sample/ScrollView Example/ScrollView-Example.storyboard +++ b/Examples/Sample/ScrollView Example/ScrollView-Example.storyboard @@ -1,7 +1,8 @@ - + - + + @@ -9,7 +10,7 @@ - + @@ -60,9 +61,4 @@ - - - - - diff --git a/Examples/Sample/TableView Example/TableView-Example.storyboard b/Examples/Sample/TableView Example/TableView-Example.storyboard index bf381ba..42e59ed 100644 --- a/Examples/Sample/TableView Example/TableView-Example.storyboard +++ b/Examples/Sample/TableView Example/TableView-Example.storyboard @@ -1,7 +1,8 @@ - + - + + @@ -11,13 +12,13 @@ - + - + - + @@ -52,10 +53,5 @@ - - - - - diff --git a/Examples/Sample/TableView Example/TableViewController.m b/Examples/Sample/TableView Example/TableViewController.m index 2135a56..c88d856 100644 --- a/Examples/Sample/TableView Example/TableViewController.m +++ b/Examples/Sample/TableView Example/TableViewController.m @@ -87,6 +87,7 @@ - (DZNSegmentedControl *)control _control.delegate = self; _control.selectedSegmentIndex = 1; _control.bouncySelectionIndicator = NO; + _control.disableSelectedSegment = NO; _control.height = 60.0f; // _control.height = 120.0f; @@ -201,6 +202,8 @@ - (void)updateControlCounts - (void)didChangeSegment:(DZNSegmentedControl *)control { + NSLog(@"%s",__FUNCTION__); + [self.tableView reloadData]; } diff --git a/Source/DZNSegmentedControl.h b/Source/DZNSegmentedControl.h index 7ef4654..f897261 100644 --- a/Source/DZNSegmentedControl.h +++ b/Source/DZNSegmentedControl.h @@ -26,7 +26,7 @@ enum { @property (nonatomic, weak) IBOutlet id delegate; /** The items displayed on the control. */ @property (nonatomic, retain) NSArray *items; -/** The index number identifying the selected segment (that is, the last segment touched). */ +/** The index number identifying the selected segment (that is, the last segment touched). Default is 0. */ @property (nonatomic) NSInteger selectedSegmentIndex; /** Returns the number of segments the receiver has. */ @property (nonatomic, readonly) NSUInteger numberOfSegments; @@ -37,7 +37,7 @@ enum { /** The height of the selection indicator. Default is 2pts. */ @property (nonatomic, readwrite) CGFloat selectionIndicatorHeight UI_APPEARANCE_SELECTOR; /** The duration of the indicator's animation. Default is 0.2 sec. */ -@property (nonatomic, readwrite) CGFloat animationDuration UI_APPEARANCE_SELECTOR; +@property (nonatomic, readwrite) NSTimeInterval animationDuration UI_APPEARANCE_SELECTOR; /** The font family to be used on labels. Default is system font (HelveticaNeue). Font size is managed by the class. */ @property (nonatomic, retain) UIFont *font UI_APPEARANCE_SELECTOR; /** The color of the hairline. Default is light gray. To hide the hairline, just set clipsToBounds to YES, like you would do it for UIToolBar & UINavigationBar. */ diff --git a/Source/DZNSegmentedControl.m b/Source/DZNSegmentedControl.m index 3d7d4ea..7fc1767 100644 --- a/Source/DZNSegmentedControl.m +++ b/Source/DZNSegmentedControl.m @@ -10,6 +10,9 @@ #import "DZNSegmentedControl.h" +@interface DZNStaticButton : UIButton +@end + @interface DZNSegmentedControl () @property (nonatomic) BOOL initializing; @@ -74,7 +77,7 @@ - (void)commonInit _initializing = YES; _showsCount = YES; - _selectedSegmentIndex = -1; + _selectedSegmentIndex = 0; _selectionIndicatorHeight = 2.0f; _animationDuration = 0.2; _autoAdjustSelectionIndicatorWidth = YES; @@ -118,10 +121,7 @@ - (void)layoutSubviews [self sizeToFit]; if ([self buttons].count == 0) { - _selectedSegmentIndex = -1; - } - else if (self.selectedSegmentIndex < 0) { - _selectedSegmentIndex = 0; + _selectedSegmentIndex = DZNSegmentedControlNoSegment; } [[self buttons] enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger idx, BOOL *stop) { @@ -188,25 +188,19 @@ - (NSUInteger)numberOfSegments - (NSArray *)buttons { - NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:self.items.count]; - - for (UIView *view in self.subviews) { - if ([view isKindOfClass:[UIButton class]]) { - [buttons addObject:view]; - } - } - return buttons; + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self isKindOfClass: %@", [DZNStaticButton class]]; + return [self.subviews filteredArrayUsingPredicate:predicate]; } -- (UIButton *)buttonAtIndex:(NSUInteger)segment +- (DZNStaticButton *)buttonAtIndex:(NSUInteger)segment { if (self.items.count > 0 && segment < [self buttons].count) { - return (UIButton *)[[self buttons] objectAtIndex:segment]; + return (DZNStaticButton *)[[self buttons] objectAtIndex:segment]; } return nil; } -- (UIButton *)selectedButton +- (DZNStaticButton *)selectedButton { if (self.selectedSegmentIndex >= 0) { return [self buttonAtIndex:self.selectedSegmentIndex]; @@ -220,7 +214,7 @@ - (NSString *)stringForSegmentAtIndex:(NSUInteger)segment return nil; } - UIButton *button = [self buttonAtIndex:segment]; + DZNStaticButton *button = [self buttonAtIndex:segment]; return [[button attributedTitleForState:UIControlStateNormal] string]; } @@ -263,7 +257,6 @@ - (UIColor *)titleColorForState:(UIControlState)state if (!color) { switch (state) { case UIControlStateNormal: return [UIColor darkGrayColor]; - case UIControlStateHighlighted: return self.tintColor; case UIControlStateDisabled: return [UIColor lightGrayColor]; case UIControlStateSelected: return self.tintColor; default: return self.tintColor; @@ -293,7 +286,12 @@ - (BOOL)autoAdjustSelectionIndicatorWidth - (CGRect)selectionIndicatorRect { - UIButton *button = [self selectedButton]; + DZNStaticButton *button = [self selectedButton]; + + if (!button) { + // Let's then grab the first button, so the selection indicator is always aligned correctly. + button = [self buttonAtIndex:0]; + } id item = self.items[button.tag]; @@ -342,7 +340,9 @@ - (UIColor *)hairlineColor - (CGRect)hairlineRect { - CGRect frame = CGRectMake(0.0f, 0.0f, self.frame.size.width, 0.5f); + CGFloat hairlineWidth = 1.0 / [UIScreen mainScreen].scale; + + CGRect frame = CGRectMake(0.0f, 0.0f, self.frame.size.width, hairlineWidth); frame.origin.y = (self.barPosition > UIBarPositionBottom) ? 0.0f : self.frame.size.height; return frame; @@ -479,7 +479,7 @@ - (void)setTintColor:(UIColor *)color if (self.isImageMode) { - for (UIButton *btn in self.buttons) { + for (DZNStaticButton *btn in self.buttons) { UIImage *normalImage = [btn imageForState:UIControlStateNormal]; UIImage *selectedImage = [normalImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; @@ -489,50 +489,19 @@ - (void)setTintColor:(UIColor *)color } } else { - [self setTitleColor:color forState:UIControlStateHighlighted]; [self setTitleColor:color forState:UIControlStateSelected]; } + + self.selectionIndicator.backgroundColor = color; } -- (void)setScrollOffset:(CGPoint)scrollOffset contentSize:(CGSize)contentSize +- (void)setHairlineColor:(UIColor *)color { - self.autoAdjustSelectionIndicatorWidth = NO; - self.bouncySelectionIndicator = NO; - - CGFloat offset = 0.0; - - // Horizontal scroll - if (self.scrollOffset.x != scrollOffset.x) { - offset = scrollOffset.x/(contentSize.width/self.numberOfSegments); - } - // Vertical scroll - else if (self.scrollOffset.y != scrollOffset.y) { - offset = scrollOffset.y/(contentSize.height/self.numberOfSegments); - } - // Skip - else { + if (self.initializing) { return; } - CGFloat buttonWidth = roundf(self.width/self.numberOfSegments); - - CGRect indicatorRect = self.selectionIndicator.frame; - indicatorRect.origin.x = (buttonWidth * offset); - self.selectionIndicator.frame = indicatorRect; - - NSUInteger index = (NSUInteger)offset; - - if (offset == truncf(offset) && self.selectedSegmentIndex != index) { - - [self unselectAllButtons]; - [self.buttons[index] setSelected:YES]; - - _selectedSegmentIndex = index; - - [self sendActionsForControlEvents:UIControlEventValueChanged]; - } - - _scrollOffset = scrollOffset; + self.hairline.backgroundColor = color; } - (void)setSelectedSegmentIndex:(NSInteger)segment @@ -549,26 +518,38 @@ - (void)setSelectedSegmentIndex:(NSInteger)segment animated:(BOOL)animated [self unselectAllButtons]; [self enableAllButtonsInteraction:YES]; - UIButton *targetButton = self.buttons[segment]; - targetButton.selected = YES; - targetButton.userInteractionEnabled = NO; - _selectedSegmentIndex = segment; + BOOL showSelectorIndicator = (segment >= 0 && segment < self.numberOfSegments); + + UIViewAnimationOptions options = UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionCurveEaseInOut; + + UIButton *button = [self buttonAtIndex:segment]; + button.selected = YES; + + if (self.disableSelectedSegment) { + button.userInteractionEnabled = NO; + } + void (^animations)() = ^void(){ - self.selectionIndicator.frame = [self selectionIndicatorRect]; + if (showSelectorIndicator) { + self.selectionIndicator.frame = [self selectionIndicatorRect]; + self.selectionIndicator.alpha = 1.0f; + } + else { + self.selectionIndicator.alpha = 0.0f; + } }; if (animated) { - CGFloat duration = (self.selectedSegmentIndex < 0.0f) ? 0.0f : self.animationDuration; CGFloat damping = !self.bouncySelectionIndicator ? : 0.65f; CGFloat velocity = !self.bouncySelectionIndicator ? : 0.5f; - [UIView animateWithDuration:duration + [UIView animateWithDuration:self.animationDuration delay:0.0f usingSpringWithDamping:damping initialSpringVelocity:velocity - options:UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionCurveEaseInOut + options:options animations:animations completion:NULL]; } @@ -588,7 +569,7 @@ - (void)setTintColor:(UIColor *)tintColor forSegmentAtIndex:(NSUInteger)segment NSAssert([tintColor isKindOfClass:[UIColor class]], @"Cannot assign a tint color with an unvalid color object."); - UIButton *button = [self buttonAtIndex:segment]; + DZNStaticButton *button = [self buttonAtIndex:segment]; if (!self.isImageMode) { button.backgroundColor = tintColor; @@ -658,16 +639,14 @@ - (void)setImage:(UIImage *)image forSegmentAtIndex:(NSUInteger)segment - (void)setAttributedTitle:(NSAttributedString *)attributedString forSegmentAtIndex:(NSUInteger)segment { - UIButton *button = [self buttonAtIndex:segment]; + DZNStaticButton *button = [self buttonAtIndex:segment]; button.titleLabel.numberOfLines = (self.showsCount) ? 2 : 1; [button setAttributedTitle:attributedString forState:UIControlStateNormal]; - [button setAttributedTitle:attributedString forState:UIControlStateHighlighted]; [button setAttributedTitle:attributedString forState:UIControlStateSelected]; [button setAttributedTitle:attributedString forState:UIControlStateDisabled]; [self setTitleColor:[self titleColorForState:UIControlStateNormal] forState:UIControlStateNormal]; - [self setTitleColor:[self titleColorForState:UIControlStateHighlighted] forState:UIControlStateHighlighted]; [self setTitleColor:[self titleColorForState:UIControlStateDisabled] forState:UIControlStateDisabled]; [self setTitleColor:[self titleColorForState:UIControlStateSelected] forState:UIControlStateSelected]; @@ -682,9 +661,9 @@ - (void)setTitleColor:(UIColor *)color forState:(UIControlState)state NSAssert([color isKindOfClass:[UIColor class]], @"Cannot assign a title color with an unvalid color object."); - for (UIButton *button in [self buttons]) { + for (DZNStaticButton *btn in [self buttons]) { - NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:[button attributedTitleForState:state]]; + NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:[btn attributedTitleForState:state]]; NSString *string = attributedString.string; NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; @@ -735,7 +714,7 @@ - (void)setTitleColor:(UIColor *)color forState:(UIControlState)state [attributedString addAttribute:NSForegroundColorAttributeName value:color range:NSMakeRange(0, attributedString.string.length)]; } - [button setAttributedTitle:attributedString forState:state]; + [btn setAttributedTitle:attributedString forState:state]; } NSString *key = [NSString stringWithFormat:@"UIControlState%d", (int)state]; @@ -788,17 +767,49 @@ - (void)setNumberFormatter:(NSNumberFormatter *)numberFormatter - (void)setEnabled:(BOOL)enabled forSegmentAtIndex:(NSUInteger)segment { - UIButton *button = [self buttonAtIndex:segment]; + DZNStaticButton *button = [self buttonAtIndex:segment]; button.enabled = enabled; } -- (void)setHairlineColor:(UIColor *)color +- (void)setScrollOffset:(CGPoint)scrollOffset contentSize:(CGSize)contentSize { - if (self.initializing) { + self.autoAdjustSelectionIndicatorWidth = NO; + self.bouncySelectionIndicator = NO; + + CGFloat offset = 0.0; + + // Horizontal scroll + if (self.scrollOffset.x != scrollOffset.x) { + offset = scrollOffset.x/(contentSize.width/self.numberOfSegments); + } + // Vertical scroll + else if (self.scrollOffset.y != scrollOffset.y) { + offset = scrollOffset.y/(contentSize.height/self.numberOfSegments); + } + // Skip + else { return; } - self.hairline.backgroundColor = color; + CGFloat buttonWidth = roundf(self.width/self.numberOfSegments); + + CGRect indicatorRect = self.selectionIndicator.frame; + indicatorRect.origin.x = (buttonWidth * offset); + self.selectionIndicator.frame = indicatorRect; + + NSUInteger index = (NSUInteger)offset; + + if (offset == truncf(offset) && self.selectedSegmentIndex != index) { + + [self unselectAllButtons]; + [self.buttons[index] setSelected:YES]; + + _selectedSegmentIndex = index; + + [self sendActionsForControlEvents:UIControlEventValueChanged]; + } + + _scrollOffset = scrollOffset; } - (void)setAdjustsButtonTopInset:(BOOL)adjustsButtonTopInset @@ -808,6 +819,14 @@ - (void)setAdjustsButtonTopInset:(BOOL)adjustsButtonTopInset [self layoutSubviews]; } +- (void)setDisableSelectedSegment:(BOOL)disableSelectedSegment +{ + _disableSelectedSegment = disableSelectedSegment; + + DZNStaticButton *button = [self selectedButton]; + button.userInteractionEnabled = !disableSelectedSegment; +} + #pragma mark - DZNSegmentedControl Configuration @@ -824,10 +843,9 @@ - (void)insertAllSegments - (void)addButtonForSegment:(NSUInteger)segment { - UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; + DZNStaticButton *button = [DZNStaticButton buttonWithType:UIButtonTypeCustom]; - [button addTarget:self action:@selector(willSelectedButton:) forControlEvents:UIControlEventTouchDown]; - [button addTarget:self action:@selector(didSelectButton:) forControlEvents:UIControlEventTouchDragOutside|UIControlEventTouchDragInside|UIControlEventTouchDragEnter|UIControlEventTouchDragExit|UIControlEventTouchCancel|UIControlEventTouchUpInside|UIControlEventTouchUpOutside]; + [button addTarget:self action:@selector(selectedButton:) forControlEvents:UIControlEventTouchDown]; button.backgroundColor = [UIColor clearColor]; button.opaque = YES; @@ -841,8 +859,8 @@ - (void)addButtonForSegment:(NSUInteger)segment - (void)configureSegments { - for (UIButton *button in [self buttons]) { - [self configureButtonForSegment:button.tag]; + for (DZNStaticButton *btn in [self buttons]) { + [self configureButtonForSegment:btn.tag]; } [self configureAccessoryViews]; @@ -902,38 +920,27 @@ - (void)configureButtonTitle:(NSString *)title forSegment:(NSUInteger)segment - (void)configureButtonImage:(UIImage *)image forSegment:(NSUInteger)segment { - UIButton *button = [self buttonAtIndex:segment]; + DZNStaticButton *button = [self buttonAtIndex:segment]; [button setImage:image forState:UIControlStateNormal]; [self setAttributedTitle:nil forSegmentAtIndex:segment]; } -- (void)willSelectedButton:(id)sender +- (void)selectedButton:(DZNStaticButton *)sender { - UIButton *button = (UIButton *)sender; + NSInteger idx = self.selectedSegmentIndex; - if (self.selectedSegmentIndex != button.tag) { - [self setSelectedSegmentIndex:button.tag animated:YES]; - [self sendActionsForControlEvents:UIControlEventValueChanged]; - } - else if (!self.disableSelectedSegment) { + [self setSelectedSegmentIndex:sender.tag animated:YES]; + + if (idx != sender.tag || !self.disableSelectedSegment) { [self sendActionsForControlEvents:UIControlEventValueChanged]; } } -- (void)didSelectButton:(id)sender -{ - UIButton *button = (UIButton *)sender; - - button.highlighted = NO; - button.selected = YES; -} - - (void)unselectAllButtons { [self.buttons setValue:@NO forKey:@"selected"]; - [self.buttons setValue:@NO forKey:@"highlighted"]; } - (void)enableAllButtonsInteraction:(BOOL)enable @@ -968,3 +975,12 @@ + (NSNumberFormatter *)defaultFormatter } @end + + +@implementation DZNStaticButton + +- (void)setHighlighted:(BOOL)highlighted { + // Let's not call super here, so there is no highlight state. +} + +@end