Skip to content

Commit ca1ae50

Browse files
committed
Merge branch 'master' of https://github.com/leontiy/HMSegmentedControl into leontiy-master
Conflicts: HMSegmentedControl.podspec HMSegmentedControl/HMSegmentedControl.h HMSegmentedControl/HMSegmentedControl.m
2 parents 81b5b98 + 2ac38f6 commit ca1ae50

File tree

5 files changed

+123
-73
lines changed

5 files changed

+123
-73
lines changed

HMSegmentedControl.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Pod::Spec.new do |s|
66
s.license = { :type => 'MIT', :file => 'LICENSE.md' }
77
s.author = { "Hesham Abd-Elmegid" => "[email protected]" }
88
s.source = { :git => "https://github.com/HeshamMegid/HMSegmentedControl.git", :tag => "v1.4.0" }
9-
s.platform = :ios, '5.0'
9+
s.platform = :ios, '7.0'
1010
s.requires_arc = true
1111
s.source_files = 'HMSegmentedControl/*.{h,m}'
1212
s.framework = 'QuartzCore'

HMSegmentedControl/HMSegmentedControl.h

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@
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 <UIKit/UIKit.h>
1010

11+
@class HMSegmentedControl;
12+
1113
typedef void (^IndexChangeBlock)(NSInteger index);
14+
typedef NSAttributedString *(^HMTitleFormatterBlock)(HMSegmentedControl *segmentedControl, NSString *title, NSUInteger index, BOOL selected);
1215

1316
typedef enum {
1417
HMSegmentedControlSelectionStyleTextWidthStripe, // Indicator width will only be as big as the text width
@@ -60,25 +63,23 @@ typedef enum {
6063
@property (nonatomic, copy) IndexChangeBlock indexChangeBlock;
6164

6265
/**
63-
Font for segments names when segmented control type is `HMSegmentedControlTypeText`
66+
Used to apply custom text styling to titles when set.
6467
65-
Default is `[UIFont fontWithName:@"STHeitiSC-Light" size:18.0f]`
68+
When this block is set, no additional styling is applied to the `NSAttributedString` object returned from this block.
6669
*/
67-
@property (nonatomic, strong) UIFont *font;
70+
@property (nonatomic, copy) HMTitleFormatterBlock titleFormatter;
6871

6972
/**
70-
Text color for segments names when segmented control type is `HMSegmentedControlTypeText`
71-
72-
Default is `[UIColor blackColor]`
73+
Text attributes to apply to item title text.
7374
*/
74-
@property (nonatomic, strong) UIColor *textColor;
75+
@property (nonatomic, strong) NSDictionary *titleTextAttributes;
7576

76-
/**
77-
Text color for selected segment name when segmented control type is `HMSegmentedControlTypeText`
77+
/*
78+
Text attributes to apply to selected item title text.
7879
79-
Default is `[UIColor blackColor]`
80+
Attributes not set in this dictionary are inherited from `titleTextAttributes`.
8081
*/
81-
@property (nonatomic, strong) UIColor *selectedTextColor;
82+
@property (nonatomic, strong) NSDictionary *selectedTitleTextAttributes;
8283

8384
/**
8485
Segmented control background color.
@@ -97,7 +98,7 @@ typedef enum {
9798
/**
9899
Color for the selection indicator stripe/box
99100
100-
Default is `self.textColor`
101+
Default is `[UIColor blackColor]`
101102
*/
102103
@property (nonatomic, strong) UIColor *verticalDividerColor;
103104

@@ -223,5 +224,6 @@ typedef enum {
223224
- (instancetype)initWithSectionImages:(NSArray *)sectionImages sectionSelectedImages:(NSArray *)sectionSelectedImages titlesForSections:(NSArray *)sectiontitles;
224225
- (void)setSelectedSegmentIndex:(NSUInteger)index animated:(BOOL)animated;
225226
- (void)setIndexChangeBlock:(IndexChangeBlock)indexChangeBlock;
227+
- (void)setTitleFormatter:(HMTitleFormatterBlock)titleFormatter;
226228

227229
@end

HMSegmentedControl/HMSegmentedControl.m

Lines changed: 97 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
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

Comments
 (0)