33// HMSegmentedControl
44//
55// Created by Hesham Abd-Elmegid on 23/12/12.
6- // Copyright (c) 2012 Hesham Abd-Elmegid. All rights reserved.
6+ // Copyright (c) 2012-2015 Hesham Abd-Elmegid. All rights reserved.
77//
88
99#import " HMSegmentedControl.h"
@@ -130,9 +130,6 @@ - (void)commonInit {
130130 self.scrollView .showsHorizontalScrollIndicator = NO ;
131131 [self addSubview: self .scrollView];
132132
133- self.font = [UIFont fontWithName: @" STHeitiSC-Light" size: 18 .0f ];
134- self.textColor = [UIColor blackColor ];
135- self.selectedTextColor = [UIColor blackColor ];
136133 self.backgroundColor = [UIColor whiteColor ];
137134 self.opaque = NO ;
138135 self.selectionIndicatorColor = [UIColor colorWithRed: 52 .0f /255 .0f green: 181 .0f /255 .0f blue: 229 .0f /255 .0f alpha: 1 .0f ];
@@ -149,6 +146,7 @@ - (void)commonInit {
149146 self.verticalDividerEnabled = NO ;
150147 self.type = HMSegmentedControlTypeText;
151148 self.verticalDividerWidth = 1 .0f ;
149+ self.verticalDividerColor = [UIColor blackColor ];
152150 self.borderColor = [UIColor blackColor ];
153151 self.borderWidth = 1 .0f ;
154152
@@ -211,8 +209,43 @@ - (void)setSegmentWidthStyle:(HMSegmentedControlSegmentWidthStyle)segmentWidthSt
211209 }
212210}
213211
212+ - (void )setBorderType : (HMSegmentedControlBorderType)borderType {
213+ _borderType = borderType;
214+ [self setNeedsDisplay ];
215+ }
216+
214217#pragma mark - Drawing
215218
219+ - (CGSize)measureTitleAtIndex : (NSUInteger )index {
220+ id title = self.sectionTitles [index];
221+ CGSize size = CGSizeZero;
222+ BOOL selected = (index == self.selectedSegmentIndex ) ? YES : NO ;
223+ if ([title isKindOfClass: [NSString class ]] && !self.titleFormatter ) {
224+ NSDictionary *titleAttrs = selected ? [self resultingSelectedTitleTextAttributes ] : [self resultingTitleTextAttributes ];
225+ size = [(NSString *)title sizeWithAttributes: titleAttrs];
226+ } else if ([title isKindOfClass: [NSString class ]] && self.titleFormatter ) {
227+ size = [self .titleFormatter (self , title, index, selected) size ];
228+ } else if ([title isKindOfClass: [NSAttributedString class ]]) {
229+ size = [(NSAttributedString *)title size ];
230+ } else {
231+ NSAssert (title == nil , @" Unexpected type of segment title: %@ " , [title class ]);
232+ size = CGSizeZero;
233+ }
234+ return CGRectIntegral ((CGRect){CGPointZero, size}).size ;
235+ }
236+
237+ - (NSAttributedString *)attributedTitleAtIndex : (NSUInteger )index {
238+ NSString *title = self.sectionTitles [index];
239+ BOOL selected = (index == self.selectedSegmentIndex ) ? YES : NO ;
240+
241+ if (!self.titleFormatter ) {
242+ NSDictionary *titleAttrs = selected ? [self resultingSelectedTitleTextAttributes ] : [self resultingTitleTextAttributes ];
243+ return [[NSAttributedString alloc ] initWithString: (NSString *)title attributes: titleAttrs];
244+ } else {
245+ return self.titleFormatter (self, title, index, selected);
246+ }
247+ }
248+
216249- (void )drawRect : (CGRect)rect {
217250 [self .backgroundColor setFill ];
218251 UIRectFill ([self bounds ]);
@@ -231,14 +264,20 @@ - (void)drawRect:(CGRect)rect {
231264
232265 if (self.type == HMSegmentedControlTypeText) {
233266 [self .sectionTitles enumerateObjectsUsingBlock: ^(id titleString, NSUInteger idx, BOOL *stop) {
234- CGFloat stringWidth = [titleString sizeWithAttributes: @{NSFontAttributeName : self.font }].width ;
235- CGFloat stringHeight = [titleString sizeWithAttributes: @{NSFontAttributeName : self.font }].height ;
236-
237- CGFloat y = roundf (CGRectGetHeight (self.frame ) - self.selectionIndicatorHeight )/2 - stringHeight/2 + ((self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationUp) ? self.selectionIndicatorHeight : 0 );
267+
268+ CGFloat stringWidth = 0 ;
269+ CGFloat stringHeight = 0 ;
270+ CGSize size = [self measureTitleAtIndex: idx];
271+ stringWidth = size.width ;
272+ stringHeight = size.height ;
273+ CGRect rectDiv, fullRect;
238274
239- CGRect rect = CGRectZero;
240- CGRect rectDiv;
241- CGRect fullRect;
275+ // Text inside the CATextLayer will appear blurry unless the rect values are rounded
276+ BOOL locationUp = (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationUp);
277+ BOOL selectionStyleNotBox = (self.selectionStyle != HMSegmentedControlSelectionStyleBox);
278+
279+ CGFloat y = roundf ((CGRectGetHeight (self.frame ) - selectionStyleNotBox * self.selectionIndicatorHeight ) / 2 - stringHeight / 2 + self.selectionIndicatorHeight * locationUp);
280+ CGRect rect;
242281 if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
243282 rect = CGRectMake ((self.segmentWidth * idx) + (self.segmentWidth - stringWidth) / 2 , y, stringWidth, stringHeight);
244283 rectDiv = CGRectMake ((self.segmentWidth * idx) - (self.verticalDividerWidth / 2 ), self.selectionIndicatorHeight * 2 , self.verticalDividerWidth , self.frame .size .height - (self.selectionIndicatorHeight * 4 ));
@@ -265,18 +304,9 @@ - (void)drawRect:(CGRect)rect {
265304
266305 CATextLayer *titleLayer = [CATextLayer layer ];
267306 titleLayer.frame = rect;
268- titleLayer.font = (__bridge CFTypeRef)(self.font .fontName );
269- titleLayer.fontSize = self.font .pointSize ;
270307 titleLayer.alignmentMode = kCAAlignmentCenter ;
271- titleLayer.string = titleString;
272308 titleLayer.truncationMode = kCATruncationEnd ;
273-
274- if (self.selectedSegmentIndex == idx) {
275- titleLayer.foregroundColor = self.selectedTextColor .CGColor ;
276- } else {
277- titleLayer.foregroundColor = self.textColor .CGColor ;
278- }
279-
309+ titleLayer.string = [self attributedTitleAtIndex: idx];
280310 titleLayer.contentsScale = [[UIScreen mainScreen ] scale ];
281311
282312 [self .scrollView.layer addSublayer: titleLayer];
@@ -285,7 +315,7 @@ - (void)drawRect:(CGRect)rect {
285315 if (self.isVerticalDividerEnabled && idx > 0 ) {
286316 CALayer *verticalDividerLayer = [CALayer layer ];
287317 verticalDividerLayer.frame = rectDiv;
288- verticalDividerLayer.backgroundColor = self.verticalDividerColor ? self. verticalDividerColor . CGColor : self. textColor .CGColor ;
318+ verticalDividerLayer.backgroundColor = self.verticalDividerColor .CGColor ;
289319
290320 [self .scrollView.layer addSublayer: verticalDividerLayer];
291321 }
@@ -320,7 +350,7 @@ - (void)drawRect:(CGRect)rect {
320350 if (self.isVerticalDividerEnabled && idx>0 ) {
321351 CALayer *verticalDividerLayer = [CALayer layer ];
322352 verticalDividerLayer.frame = CGRectMake ((self.segmentWidth * idx) - (self.verticalDividerWidth / 2 ), self.selectionIndicatorHeight * 2 , self.verticalDividerWidth , self.frame .size .height -(self.selectionIndicatorHeight * 4 ));
323- verticalDividerLayer.backgroundColor = self.verticalDividerColor ? self. verticalDividerColor . CGColor : self. textColor .CGColor ;
353+ verticalDividerLayer.backgroundColor = self.verticalDividerColor .CGColor ;
324354
325355 [self .scrollView.layer addSublayer: verticalDividerLayer];
326356 }
@@ -333,9 +363,8 @@ - (void)drawRect:(CGRect)rect {
333363 CGFloat imageWidth = icon.size .width ;
334364 CGFloat imageHeight = icon.size .height ;
335365
336- CGFloat stringHeight = roundf ([self .sectionTitles[idx] sizeWithAttributes: @{NSFontAttributeName : self.font }].height );
337-
338- CGFloat yOffset = roundf ((CGRectGetHeight (self.frame ) - self.selectionIndicatorHeight ) / 2 .0f ) - stringHeight;
366+ CGFloat stringHeight = [self measureTitleAtIndex: idx].height ;
367+ CGFloat yOffset = roundf (((CGRectGetHeight (self.frame ) - self.selectionIndicatorHeight ) / 2 ) - (stringHeight / 2 ));
339368
340369 CGFloat imageXOffset = self.segmentEdgeInset .left ; // Start with edge inset
341370 CGFloat textXOffset = self.segmentEdgeInset .left ;
@@ -373,10 +402,8 @@ - (void)drawRect:(CGRect)rect {
373402
374403 CATextLayer *titleLayer = [CATextLayer layer ];
375404 titleLayer.frame = textRect;
376- titleLayer.font = (__bridge CFTypeRef)(self.font .fontName );
377- titleLayer.fontSize = self.font .pointSize ;
378405 titleLayer.alignmentMode = kCAAlignmentCenter ;
379- titleLayer.string = self. sectionTitles [ idx];
406+ titleLayer.string = [ self attributedTitleAtIndex: idx];
380407 titleLayer.truncationMode = kCATruncationEnd ;
381408
382409 CALayer *imageLayer = [CALayer layer ];
@@ -389,10 +416,8 @@ - (void)drawRect:(CGRect)rect {
389416 } else {
390417 imageLayer.contents = (id )icon.CGImage ;
391418 }
392- titleLayer.foregroundColor = self.selectedTextColor .CGColor ;
393419 } else {
394420 imageLayer.contents = (id )icon.CGImage ;
395- titleLayer.foregroundColor = self.textColor .CGColor ;
396421 }
397422
398423 [self .scrollView.layer addSublayer: imageLayer];
@@ -505,14 +530,14 @@ - (CGRect)frameForSelectionIndicator {
505530 CGFloat sectionWidth = 0 .0f ;
506531
507532 if (self.type == HMSegmentedControlTypeText) {
508- CGFloat stringWidth = [[ self .sectionTitles objectAtIndex :self .selectedSegmentIndex] sizeWithAttributes: @{ NSFontAttributeName : self. font } ].width ;
533+ CGFloat stringWidth = [self measureTitleAtIndex :self .selectedSegmentIndex].width ;
509534 sectionWidth = stringWidth;
510535 } else if (self.type == HMSegmentedControlTypeImages) {
511536 UIImage *sectionImage = [self .sectionImages objectAtIndex: self .selectedSegmentIndex];
512537 CGFloat imageWidth = sectionImage.size .width ;
513538 sectionWidth = imageWidth;
514539 } else if (self.type == HMSegmentedControlTypeTextImages) {
515- CGFloat stringWidth = [[ self .sectionTitles objectAtIndex :self .selectedSegmentIndex] sizeWithAttributes: @{ NSFontAttributeName : self. font } ].width ;
540+ CGFloat stringWidth = [self measureTitleAtIndex :self .selectedSegmentIndex].width ;
516541 UIImage *sectionImage = [self .sectionImages objectAtIndex: self .selectedSegmentIndex];
517542 CGFloat imageWidth = sectionImage.size .width ;
518543 sectionWidth = MAX (stringWidth, imageWidth);
@@ -580,17 +605,17 @@ - (void)updateSegmentsRects {
580605 }
581606
582607 if (self.type == HMSegmentedControlTypeText && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
583- for ( NSString * titleString in self. sectionTitles ) {
584- CGFloat stringWidth = [titleString sizeWithAttributes: @{ NSFontAttributeName : self. font } ].width + self.segmentEdgeInset .left + self.segmentEdgeInset .right ;
608+ [ self .sectionTitles enumerateObjectsUsingBlock: ^( id titleString, NSUInteger idx, BOOL *stop ) {
609+ CGFloat stringWidth = [self measureTitleAtIndex: idx ].width + self.segmentEdgeInset .left + self.segmentEdgeInset .right ;
585610 self.segmentWidth = MAX (stringWidth, self.segmentWidth );
586- }
611+ }];
587612 } else if (self.type == HMSegmentedControlTypeText && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
588613 NSMutableArray *mutableSegmentWidths = [NSMutableArray array ];
589614
590- for ( NSString * titleString in self. sectionTitles ) {
591- CGFloat stringWidth = [titleString sizeWithAttributes: @{ NSFontAttributeName : self. font } ].width + self.segmentEdgeInset .left + self.segmentEdgeInset .right ;
615+ [ self .sectionTitles enumerateObjectsUsingBlock: ^( id titleString, NSUInteger idx, BOOL *stop ) {
616+ CGFloat stringWidth = [self measureTitleAtIndex: idx ].width + self.segmentEdgeInset .left + self.segmentEdgeInset .right ;
592617 [mutableSegmentWidths addObject: [NSNumber numberWithFloat: stringWidth]];
593- }
618+ }];
594619 self.segmentWidthsArray = [mutableSegmentWidths copy ];
595620 } else if (self.type == HMSegmentedControlTypeImages) {
596621 for (UIImage *sectionImage in self.sectionImages ) {
@@ -599,25 +624,23 @@ - (void)updateSegmentsRects {
599624 }
600625 } else if (self.type == HMSegmentedControlTypeTextImages && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed){
601626 // lets just use the title.. we will assume it is wider then images...
602- for ( NSString * titleString in self. sectionTitles ) {
603- CGFloat stringWidth = [titleString sizeWithAttributes: @{ NSFontAttributeName : self. font } ].width + self.segmentEdgeInset .left + self.segmentEdgeInset .right ;
627+ [ self .sectionTitles enumerateObjectsUsingBlock: ^( id titleString, NSUInteger idx, BOOL *stop ) {
628+ CGFloat stringWidth = [self measureTitleAtIndex: idx ].width + self.segmentEdgeInset .left + self.segmentEdgeInset .right ;
604629 self.segmentWidth = MAX (stringWidth, self.segmentWidth );
605- }
606- } else if (self.type == HMSegmentedControlTypeTextImages && self. segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
630+ }];
631+ } else if (self.type == HMSegmentedControlTypeTextImages && HMSegmentedControlSegmentWidthStyleDynamic) {
607632 NSMutableArray *mutableSegmentWidths = [NSMutableArray array ];
608633
609634 int i = 0 ;
610- for ( NSString * titleString in self. sectionTitles ) {
611- CGFloat stringWidth = [titleString sizeWithAttributes: @{ NSFontAttributeName : self. font } ].width + self.segmentEdgeInset .right ;
635+ [ self .sectionTitles enumerateObjectsUsingBlock: ^( id titleString, NSUInteger idx, BOOL *stop ) {
636+ CGFloat stringWidth = [self measureTitleAtIndex: idx ].width + self.segmentEdgeInset .right ;
612637 UIImage *sectionImage = [self .sectionImages objectAtIndex: i];
613638 CGFloat imageWidth = sectionImage.size .width + self.segmentEdgeInset .left ;
614639
615640 CGFloat combinedWidth = MAX (imageWidth, stringWidth);
616641
617642 [mutableSegmentWidths addObject: [NSNumber numberWithFloat: combinedWidth]];
618-
619- i++;
620- }
643+ }];
621644 self.segmentWidthsArray = [mutableSegmentWidths copy ];
622645 }
623646
@@ -733,7 +756,7 @@ - (void)scrollToSelectedSegmentIndex:(BOOL)animated {
733756 [self .scrollView scrollRectToVisible: rectToScrollTo animated: animated];
734757}
735758
736- #pragma mark - Index change
759+ #pragma mark - Index Change
737760
738761- (void )setSelectedSegmentIndex : (NSInteger )index {
739762 [self setSelectedSegmentIndex: index animated: NO notify: NO ];
@@ -820,9 +843,31 @@ - (void)notifyForSegmentChangeToIndex:(NSInteger)index {
820843 self.indexChangeBlock (index);
821844}
822845
823- - (void )setBorderType : (HMSegmentedControlBorderType)borderType {
824- _borderType = borderType;
825- [self setNeedsDisplay ];
846+ #pragma mark - Styling Support
847+
848+ - (NSDictionary *)resultingTitleTextAttributes {
849+ NSDictionary *defaults = @{
850+ NSFontAttributeName : [UIFont fontWithName: @" STHeitiSC-Light" size: 18 .0f ],
851+ NSForegroundColorAttributeName : [UIColor blackColor ],
852+ };
853+
854+ NSMutableDictionary *resultingAttrs = [NSMutableDictionary dictionaryWithDictionary: defaults];
855+
856+ if (self.titleTextAttributes ) {
857+ [resultingAttrs addEntriesFromDictionary: self .titleTextAttributes];
858+ }
859+
860+ return [resultingAttrs copy ];
861+ }
862+
863+ - (NSDictionary *)resultingSelectedTitleTextAttributes {
864+ NSMutableDictionary *resultingAttrs = [NSMutableDictionary dictionaryWithDictionary: [self resultingTitleTextAttributes ]];
865+
866+ if (self.selectedTitleTextAttributes ) {
867+ [resultingAttrs addEntriesFromDictionary: self .selectedTitleTextAttributes];
868+ }
869+
870+ return [resultingAttrs copy ];
826871}
827872
828873@end
0 commit comments