|
@@ -0,0 +1,958 @@
|
|
1
|
+//
|
|
2
|
+// HMSegmentedControl.m
|
|
3
|
+// HMSegmentedControl
|
|
4
|
+//
|
|
5
|
+// Created by Hesham Abd-Elmegid on 23/12/12.
|
|
6
|
+// Copyright (c) 2012-2015 Hesham Abd-Elmegid. All rights reserved.
|
|
7
|
+//
|
|
8
|
+
|
|
9
|
+#import "HMSegmentedControl.h"
|
|
10
|
+#import <QuartzCore/QuartzCore.h>
|
|
11
|
+#import <math.h>
|
|
12
|
+
|
|
13
|
+@interface HMScrollView : UIScrollView
|
|
14
|
+@end
|
|
15
|
+
|
|
16
|
+@interface HMSegmentedControl ()
|
|
17
|
+
|
|
18
|
+@property (nonatomic, strong) CALayer *selectionIndicatorStripLayer;
|
|
19
|
+@property (nonatomic, strong) CALayer *selectionIndicatorBoxLayer;
|
|
20
|
+@property (nonatomic, strong) CALayer *selectionIndicatorArrowLayer;
|
|
21
|
+@property (nonatomic, readwrite) CGFloat segmentWidth;
|
|
22
|
+@property (nonatomic, readwrite) NSArray<NSNumber *> *segmentWidthsArray;
|
|
23
|
+@property (nonatomic, strong) HMScrollView *scrollView;
|
|
24
|
+
|
|
25
|
+@end
|
|
26
|
+
|
|
27
|
+@implementation HMScrollView
|
|
28
|
+
|
|
29
|
+- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
|
|
30
|
+ if (!self.dragging) {
|
|
31
|
+ [self.nextResponder touchesBegan:touches withEvent:event];
|
|
32
|
+ } else {
|
|
33
|
+ [super touchesBegan:touches withEvent:event];
|
|
34
|
+ }
|
|
35
|
+}
|
|
36
|
+
|
|
37
|
+- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
|
|
38
|
+ if (!self.dragging) {
|
|
39
|
+ [self.nextResponder touchesMoved:touches withEvent:event];
|
|
40
|
+ } else{
|
|
41
|
+ [super touchesMoved:touches withEvent:event];
|
|
42
|
+ }
|
|
43
|
+}
|
|
44
|
+
|
|
45
|
+- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
|
|
46
|
+ if (!self.dragging) {
|
|
47
|
+ [self.nextResponder touchesEnded:touches withEvent:event];
|
|
48
|
+ } else {
|
|
49
|
+ [super touchesEnded:touches withEvent:event];
|
|
50
|
+ }
|
|
51
|
+}
|
|
52
|
+
|
|
53
|
+@end
|
|
54
|
+
|
|
55
|
+@implementation HMSegmentedControl
|
|
56
|
+
|
|
57
|
+- (id)initWithCoder:(NSCoder *)aDecoder {
|
|
58
|
+ self = [super initWithCoder:aDecoder];
|
|
59
|
+ if (self) {
|
|
60
|
+ [self commonInit];
|
|
61
|
+ }
|
|
62
|
+ return self;
|
|
63
|
+}
|
|
64
|
+
|
|
65
|
+- (id)initWithFrame:(CGRect)frame {
|
|
66
|
+ self = [super initWithFrame:frame];
|
|
67
|
+
|
|
68
|
+ if (self) {
|
|
69
|
+ [self commonInit];
|
|
70
|
+ }
|
|
71
|
+
|
|
72
|
+ return self;
|
|
73
|
+}
|
|
74
|
+
|
|
75
|
+- (id)initWithSectionTitles:(NSArray<NSString *> *)sectiontitles {
|
|
76
|
+ self = [super initWithFrame:CGRectZero];
|
|
77
|
+
|
|
78
|
+ if (self) {
|
|
79
|
+ [self commonInit];
|
|
80
|
+ self.sectionTitles = sectiontitles;
|
|
81
|
+ self.type = HMSegmentedControlTypeText;
|
|
82
|
+ }
|
|
83
|
+
|
|
84
|
+ return self;
|
|
85
|
+}
|
|
86
|
+
|
|
87
|
+- (id)initWithSectionImages:(NSArray<UIImage *> *)sectionImages sectionSelectedImages:(NSArray<UIImage *> *)sectionSelectedImages {
|
|
88
|
+ self = [super initWithFrame:CGRectZero];
|
|
89
|
+
|
|
90
|
+ if (self) {
|
|
91
|
+ [self commonInit];
|
|
92
|
+ self.sectionImages = sectionImages;
|
|
93
|
+ self.sectionSelectedImages = sectionSelectedImages;
|
|
94
|
+ self.type = HMSegmentedControlTypeImages;
|
|
95
|
+ }
|
|
96
|
+
|
|
97
|
+ return self;
|
|
98
|
+}
|
|
99
|
+
|
|
100
|
+- (instancetype)initWithSectionImages:(NSArray<UIImage *> *)sectionImages sectionSelectedImages:(NSArray<UIImage *> *)sectionSelectedImages titlesForSections:(NSArray<NSString *> *)sectiontitles {
|
|
101
|
+ self = [super initWithFrame:CGRectZero];
|
|
102
|
+
|
|
103
|
+ if (self) {
|
|
104
|
+ [self commonInit];
|
|
105
|
+
|
|
106
|
+ if (sectionImages.count != sectiontitles.count) {
|
|
107
|
+ [NSException raise:NSRangeException format:@"***%s: Images bounds (%ld) Don't match Title bounds (%ld)", sel_getName(_cmd), (unsigned long)sectionImages.count, (unsigned long)sectiontitles.count];
|
|
108
|
+ }
|
|
109
|
+
|
|
110
|
+ self.sectionImages = sectionImages;
|
|
111
|
+ self.sectionSelectedImages = sectionSelectedImages;
|
|
112
|
+ self.sectionTitles = sectiontitles;
|
|
113
|
+ self.type = HMSegmentedControlTypeTextImages;
|
|
114
|
+ }
|
|
115
|
+
|
|
116
|
+ return self;
|
|
117
|
+}
|
|
118
|
+
|
|
119
|
+- (void)awakeFromNib {
|
|
120
|
+ [super awakeFromNib];
|
|
121
|
+
|
|
122
|
+ self.segmentWidth = 0.0f;
|
|
123
|
+}
|
|
124
|
+
|
|
125
|
+- (void)commonInit {
|
|
126
|
+ self.scrollView = [[HMScrollView alloc] init];
|
|
127
|
+ self.scrollView.scrollsToTop = NO;
|
|
128
|
+ self.scrollView.showsVerticalScrollIndicator = NO;
|
|
129
|
+ self.scrollView.showsHorizontalScrollIndicator = NO;
|
|
130
|
+ [self addSubview:self.scrollView];
|
|
131
|
+
|
|
132
|
+ _backgroundColor = [UIColor whiteColor];
|
|
133
|
+ self.opaque = NO;
|
|
134
|
+ _selectionIndicatorColor = [UIColor colorWithRed:52.0f/255.0f green:181.0f/255.0f blue:229.0f/255.0f alpha:1.0f];
|
|
135
|
+ _selectionIndicatorBoxColor = _selectionIndicatorColor;
|
|
136
|
+
|
|
137
|
+ self.selectedSegmentIndex = 0;
|
|
138
|
+ self.segmentEdgeInset = UIEdgeInsetsMake(0, 5, 0, 5);
|
|
139
|
+ self.selectionIndicatorHeight = 5.0f;
|
|
140
|
+ self.selectionIndicatorEdgeInsets = UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f);
|
|
141
|
+ self.selectionStyle = HMSegmentedControlSelectionStyleTextWidthStripe;
|
|
142
|
+ self.selectionIndicatorLocation = HMSegmentedControlSelectionIndicatorLocationUp;
|
|
143
|
+ self.segmentWidthStyle = HMSegmentedControlSegmentWidthStyleFixed;
|
|
144
|
+ self.userDraggable = YES;
|
|
145
|
+ self.touchEnabled = YES;
|
|
146
|
+ self.verticalDividerEnabled = NO;
|
|
147
|
+ self.type = HMSegmentedControlTypeText;
|
|
148
|
+ self.verticalDividerWidth = 1.0f;
|
|
149
|
+ _verticalDividerColor = [UIColor blackColor];
|
|
150
|
+ self.borderColor = [UIColor blackColor];
|
|
151
|
+ self.borderWidth = 1.0f;
|
|
152
|
+
|
|
153
|
+ self.shouldAnimateUserSelection = YES;
|
|
154
|
+
|
|
155
|
+ self.selectionIndicatorArrowLayer = [CALayer layer];
|
|
156
|
+ self.selectionIndicatorStripLayer = [CALayer layer];
|
|
157
|
+ self.selectionIndicatorBoxLayer = [CALayer layer];
|
|
158
|
+ self.selectionIndicatorBoxLayer.opacity = self.selectionIndicatorBoxOpacity;
|
|
159
|
+ self.selectionIndicatorBoxLayer.borderWidth = 1.0f;
|
|
160
|
+ self.selectionIndicatorBoxOpacity = 0.2;
|
|
161
|
+
|
|
162
|
+ self.contentMode = UIViewContentModeRedraw;
|
|
163
|
+}
|
|
164
|
+
|
|
165
|
+- (void)layoutSubviews {
|
|
166
|
+ [super layoutSubviews];
|
|
167
|
+
|
|
168
|
+ [self updateSegmentsRects];
|
|
169
|
+}
|
|
170
|
+
|
|
171
|
+- (void)setFrame:(CGRect)frame {
|
|
172
|
+ [super setFrame:frame];
|
|
173
|
+
|
|
174
|
+ [self updateSegmentsRects];
|
|
175
|
+}
|
|
176
|
+
|
|
177
|
+- (void)setSectionTitles:(NSArray<NSString *> *)sectionTitles {
|
|
178
|
+ _sectionTitles = sectionTitles;
|
|
179
|
+
|
|
180
|
+ [self setNeedsLayout];
|
|
181
|
+ [self setNeedsDisplay];
|
|
182
|
+}
|
|
183
|
+
|
|
184
|
+- (void)setSectionImages:(NSArray<UIImage *> *)sectionImages {
|
|
185
|
+ _sectionImages = sectionImages;
|
|
186
|
+
|
|
187
|
+ [self setNeedsLayout];
|
|
188
|
+ [self setNeedsDisplay];
|
|
189
|
+}
|
|
190
|
+
|
|
191
|
+- (void)setSelectionIndicatorLocation:(HMSegmentedControlSelectionIndicatorLocation)selectionIndicatorLocation {
|
|
192
|
+ _selectionIndicatorLocation = selectionIndicatorLocation;
|
|
193
|
+
|
|
194
|
+ if (selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationNone) {
|
|
195
|
+ self.selectionIndicatorHeight = 0.0f;
|
|
196
|
+ }
|
|
197
|
+}
|
|
198
|
+
|
|
199
|
+- (void)setSelectionIndicatorBoxOpacity:(CGFloat)selectionIndicatorBoxOpacity {
|
|
200
|
+ _selectionIndicatorBoxOpacity = selectionIndicatorBoxOpacity;
|
|
201
|
+
|
|
202
|
+ self.selectionIndicatorBoxLayer.opacity = _selectionIndicatorBoxOpacity;
|
|
203
|
+}
|
|
204
|
+
|
|
205
|
+- (void)setSegmentWidthStyle:(HMSegmentedControlSegmentWidthStyle)segmentWidthStyle {
|
|
206
|
+ // Force HMSegmentedControlSegmentWidthStyleFixed when type is HMSegmentedControlTypeImages.
|
|
207
|
+ if (self.type == HMSegmentedControlTypeImages) {
|
|
208
|
+ _segmentWidthStyle = HMSegmentedControlSegmentWidthStyleFixed;
|
|
209
|
+ } else {
|
|
210
|
+ _segmentWidthStyle = segmentWidthStyle;
|
|
211
|
+ }
|
|
212
|
+}
|
|
213
|
+
|
|
214
|
+- (void)setBorderType:(HMSegmentedControlBorderType)borderType {
|
|
215
|
+ _borderType = borderType;
|
|
216
|
+ [self setNeedsDisplay];
|
|
217
|
+}
|
|
218
|
+
|
|
219
|
+#pragma mark - Drawing
|
|
220
|
+
|
|
221
|
+- (CGSize)measureTitleAtIndex:(NSUInteger)index {
|
|
222
|
+ if (index >= self.sectionTitles.count) {
|
|
223
|
+ return CGSizeZero;
|
|
224
|
+ }
|
|
225
|
+
|
|
226
|
+ id title = self.sectionTitles[index];
|
|
227
|
+ CGSize size = CGSizeZero;
|
|
228
|
+ BOOL selected = (index == self.selectedSegmentIndex) ? YES : NO;
|
|
229
|
+ if ([title isKindOfClass:[NSString class]] && !self.titleFormatter) {
|
|
230
|
+ NSDictionary *titleAttrs = selected ? [self resultingSelectedTitleTextAttributes] : [self resultingTitleTextAttributes];
|
|
231
|
+ size = [(NSString *)title sizeWithAttributes:titleAttrs];
|
|
232
|
+ } else if ([title isKindOfClass:[NSString class]] && self.titleFormatter) {
|
|
233
|
+ size = [self.titleFormatter(self, title, index, selected) size];
|
|
234
|
+ } else if ([title isKindOfClass:[NSAttributedString class]]) {
|
|
235
|
+ size = [(NSAttributedString *)title size];
|
|
236
|
+ } else {
|
|
237
|
+ NSAssert(title == nil, @"Unexpected type of segment title: %@", [title class]);
|
|
238
|
+ size = CGSizeZero;
|
|
239
|
+ }
|
|
240
|
+ return CGRectIntegral((CGRect){CGPointZero, size}).size;
|
|
241
|
+}
|
|
242
|
+
|
|
243
|
+- (NSAttributedString *)attributedTitleAtIndex:(NSUInteger)index {
|
|
244
|
+ id title = self.sectionTitles[index];
|
|
245
|
+ BOOL selected = (index == self.selectedSegmentIndex) ? YES : NO;
|
|
246
|
+
|
|
247
|
+ if ([title isKindOfClass:[NSAttributedString class]]) {
|
|
248
|
+ return (NSAttributedString *)title;
|
|
249
|
+ } else if (!self.titleFormatter) {
|
|
250
|
+ NSDictionary *titleAttrs = selected ? [self resultingSelectedTitleTextAttributes] : [self resultingTitleTextAttributes];
|
|
251
|
+
|
|
252
|
+ // the color should be cast to CGColor in order to avoid invalid context on iOS7
|
|
253
|
+ UIColor *titleColor = titleAttrs[NSForegroundColorAttributeName];
|
|
254
|
+
|
|
255
|
+ if (titleColor) {
|
|
256
|
+ NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:titleAttrs];
|
|
257
|
+
|
|
258
|
+ dict[NSForegroundColorAttributeName] = (id)titleColor.CGColor;
|
|
259
|
+
|
|
260
|
+ titleAttrs = [NSDictionary dictionaryWithDictionary:dict];
|
|
261
|
+ }
|
|
262
|
+
|
|
263
|
+ return [[NSAttributedString alloc] initWithString:(NSString *)title attributes:titleAttrs];
|
|
264
|
+ } else {
|
|
265
|
+ return self.titleFormatter(self, title, index, selected);
|
|
266
|
+ }
|
|
267
|
+}
|
|
268
|
+
|
|
269
|
+- (void)drawRect:(CGRect)rect {
|
|
270
|
+ [self.backgroundColor setFill];
|
|
271
|
+ UIRectFill([self bounds]);
|
|
272
|
+
|
|
273
|
+ self.selectionIndicatorArrowLayer.backgroundColor = self.selectionIndicatorColor.CGColor;
|
|
274
|
+
|
|
275
|
+ self.selectionIndicatorStripLayer.backgroundColor = self.selectionIndicatorColor.CGColor;
|
|
276
|
+
|
|
277
|
+ self.selectionIndicatorBoxLayer.backgroundColor = self.selectionIndicatorBoxColor.CGColor;
|
|
278
|
+ self.selectionIndicatorBoxLayer.borderColor = self.selectionIndicatorBoxColor.CGColor;
|
|
279
|
+
|
|
280
|
+ // Remove all sublayers to avoid drawing images over existing ones
|
|
281
|
+ self.scrollView.layer.sublayers = nil;
|
|
282
|
+
|
|
283
|
+ CGRect oldRect = rect;
|
|
284
|
+
|
|
285
|
+ if (self.type == HMSegmentedControlTypeText) {
|
|
286
|
+ [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx, BOOL *stop) {
|
|
287
|
+
|
|
288
|
+ CGFloat stringWidth = 0;
|
|
289
|
+ CGFloat stringHeight = 0;
|
|
290
|
+ CGSize size = [self measureTitleAtIndex:idx];
|
|
291
|
+ stringWidth = size.width;
|
|
292
|
+ stringHeight = size.height;
|
|
293
|
+ CGRect rectDiv = CGRectZero;
|
|
294
|
+ CGRect fullRect = CGRectZero;
|
|
295
|
+
|
|
296
|
+ // Text inside the CATextLayer will appear blurry unless the rect values are rounded
|
|
297
|
+ BOOL locationUp = (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationUp);
|
|
298
|
+ BOOL selectionStyleNotBox = (self.selectionStyle != HMSegmentedControlSelectionStyleBox);
|
|
299
|
+
|
|
300
|
+ CGFloat y = roundf((CGRectGetHeight(self.frame) - selectionStyleNotBox * self.selectionIndicatorHeight) / 2 - stringHeight / 2 + self.selectionIndicatorHeight * locationUp);
|
|
301
|
+ CGRect rect;
|
|
302
|
+ if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
|
|
303
|
+ rect = CGRectMake((self.segmentWidth * idx) + (self.segmentWidth - stringWidth) / 2, y, stringWidth, stringHeight);
|
|
304
|
+ rectDiv = CGRectMake((self.segmentWidth * idx) - (self.verticalDividerWidth / 2), self.selectionIndicatorHeight * 2, self.verticalDividerWidth, self.frame.size.height - (self.selectionIndicatorHeight * 4));
|
|
305
|
+ fullRect = CGRectMake(self.segmentWidth * idx, 0, self.segmentWidth, oldRect.size.height);
|
|
306
|
+ } else {
|
|
307
|
+ // When we are drawing dynamic widths, we need to loop the widths array to calculate the xOffset
|
|
308
|
+ CGFloat xOffset = 0;
|
|
309
|
+ NSInteger i = 0;
|
|
310
|
+ for (NSNumber *width in self.segmentWidthsArray) {
|
|
311
|
+ if (idx == i)
|
|
312
|
+ break;
|
|
313
|
+ xOffset = xOffset + [width floatValue];
|
|
314
|
+ i++;
|
|
315
|
+ }
|
|
316
|
+
|
|
317
|
+ CGFloat widthForIndex = [[self.segmentWidthsArray objectAtIndex:idx] floatValue];
|
|
318
|
+ rect = CGRectMake(xOffset, y, widthForIndex, stringHeight);
|
|
319
|
+ fullRect = CGRectMake(self.segmentWidth * idx, 0, widthForIndex, oldRect.size.height);
|
|
320
|
+ rectDiv = CGRectMake(xOffset - (self.verticalDividerWidth / 2), self.selectionIndicatorHeight * 2, self.verticalDividerWidth, self.frame.size.height - (self.selectionIndicatorHeight * 4));
|
|
321
|
+ }
|
|
322
|
+
|
|
323
|
+ // Fix rect position/size to avoid blurry labels
|
|
324
|
+ rect = CGRectMake(ceilf(rect.origin.x), ceilf(rect.origin.y), ceilf(rect.size.width), ceilf(rect.size.height));
|
|
325
|
+
|
|
326
|
+ CATextLayer *titleLayer = [CATextLayer layer];
|
|
327
|
+ titleLayer.frame = rect;
|
|
328
|
+ titleLayer.alignmentMode = kCAAlignmentCenter;
|
|
329
|
+ if ([UIDevice currentDevice].systemVersion.floatValue < 10.0 ) {
|
|
330
|
+ titleLayer.truncationMode = kCATruncationEnd;
|
|
331
|
+ }
|
|
332
|
+ titleLayer.string = [self attributedTitleAtIndex:idx];
|
|
333
|
+ titleLayer.contentsScale = [[UIScreen mainScreen] scale];
|
|
334
|
+
|
|
335
|
+ [self.scrollView.layer addSublayer:titleLayer];
|
|
336
|
+
|
|
337
|
+ // Vertical Divider
|
|
338
|
+ if (self.isVerticalDividerEnabled && idx > 0) {
|
|
339
|
+ CALayer *verticalDividerLayer = [CALayer layer];
|
|
340
|
+ verticalDividerLayer.frame = rectDiv;
|
|
341
|
+ verticalDividerLayer.backgroundColor = self.verticalDividerColor.CGColor;
|
|
342
|
+
|
|
343
|
+ [self.scrollView.layer addSublayer:verticalDividerLayer];
|
|
344
|
+ }
|
|
345
|
+
|
|
346
|
+ [self addBackgroundAndBorderLayerWithRect:fullRect];
|
|
347
|
+ }];
|
|
348
|
+ } else if (self.type == HMSegmentedControlTypeImages) {
|
|
349
|
+ [self.sectionImages enumerateObjectsUsingBlock:^(id iconImage, NSUInteger idx, BOOL *stop) {
|
|
350
|
+ UIImage *icon = iconImage;
|
|
351
|
+ CGFloat imageWidth = icon.size.width;
|
|
352
|
+ CGFloat imageHeight = icon.size.height;
|
|
353
|
+ CGFloat y = roundf(CGRectGetHeight(self.frame) - self.selectionIndicatorHeight) / 2 - imageHeight / 2 + ((self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationUp) ? self.selectionIndicatorHeight : 0);
|
|
354
|
+ CGFloat x = self.segmentWidth * idx + (self.segmentWidth - imageWidth)/2.0f;
|
|
355
|
+ CGRect rect = CGRectMake(x, y, imageWidth, imageHeight);
|
|
356
|
+
|
|
357
|
+ CALayer *imageLayer = [CALayer layer];
|
|
358
|
+ imageLayer.frame = rect;
|
|
359
|
+
|
|
360
|
+ if (self.selectedSegmentIndex == idx) {
|
|
361
|
+ if (self.sectionSelectedImages) {
|
|
362
|
+ UIImage *highlightIcon = [self.sectionSelectedImages objectAtIndex:idx];
|
|
363
|
+ imageLayer.contents = (id)highlightIcon.CGImage;
|
|
364
|
+ } else {
|
|
365
|
+ imageLayer.contents = (id)icon.CGImage;
|
|
366
|
+ }
|
|
367
|
+ } else {
|
|
368
|
+ imageLayer.contents = (id)icon.CGImage;
|
|
369
|
+ }
|
|
370
|
+
|
|
371
|
+ [self.scrollView.layer addSublayer:imageLayer];
|
|
372
|
+ // Vertical Divider
|
|
373
|
+ if (self.isVerticalDividerEnabled && idx>0) {
|
|
374
|
+ CALayer *verticalDividerLayer = [CALayer layer];
|
|
375
|
+ verticalDividerLayer.frame = CGRectMake((self.segmentWidth * idx) - (self.verticalDividerWidth / 2), self.selectionIndicatorHeight * 2, self.verticalDividerWidth, self.frame.size.height-(self.selectionIndicatorHeight * 4));
|
|
376
|
+ verticalDividerLayer.backgroundColor = self.verticalDividerColor.CGColor;
|
|
377
|
+
|
|
378
|
+ [self.scrollView.layer addSublayer:verticalDividerLayer];
|
|
379
|
+ }
|
|
380
|
+
|
|
381
|
+ [self addBackgroundAndBorderLayerWithRect:rect];
|
|
382
|
+ }];
|
|
383
|
+ } else if (self.type == HMSegmentedControlTypeTextImages){
|
|
384
|
+ [self.sectionImages enumerateObjectsUsingBlock:^(id iconImage, NSUInteger idx, BOOL *stop) {
|
|
385
|
+ UIImage *icon = iconImage;
|
|
386
|
+ CGFloat imageWidth = icon.size.width;
|
|
387
|
+ CGFloat imageHeight = icon.size.height;
|
|
388
|
+
|
|
389
|
+ CGSize stringSize = [self measureTitleAtIndex:idx];
|
|
390
|
+ CGFloat stringHeight = stringSize.height;
|
|
391
|
+ CGFloat stringWidth = stringSize.width;
|
|
392
|
+
|
|
393
|
+ CGFloat imageXOffset = self.segmentWidth * idx; // Start with edge inset
|
|
394
|
+ CGFloat textXOffset = self.segmentWidth * idx;
|
|
395
|
+ CGFloat imageYOffset = ceilf((self.frame.size.height - imageHeight) / 2.0); // Start in center
|
|
396
|
+ CGFloat textYOffset = ceilf((self.frame.size.height - stringHeight) / 2.0);
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+ if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
|
|
400
|
+ BOOL isImageInLineWidthText = self.imagePosition == HMSegmentedControlImagePositionLeftOfText || self.imagePosition == HMSegmentedControlImagePositionRightOfText;
|
|
401
|
+ if (isImageInLineWidthText) {
|
|
402
|
+ CGFloat whitespace = self.segmentWidth - stringSize.width - imageWidth - self.textImageSpacing;
|
|
403
|
+ if (self.imagePosition == HMSegmentedControlImagePositionLeftOfText) {
|
|
404
|
+ imageXOffset += whitespace / 2.0;
|
|
405
|
+ textXOffset = imageXOffset + imageWidth + self.textImageSpacing;
|
|
406
|
+ } else {
|
|
407
|
+ textXOffset += whitespace / 2.0;
|
|
408
|
+ imageXOffset = textXOffset + stringWidth + self.textImageSpacing;
|
|
409
|
+ }
|
|
410
|
+ } else {
|
|
411
|
+ imageXOffset = self.segmentWidth * idx + (self.segmentWidth - imageWidth) / 2.0f; // Start with edge inset
|
|
412
|
+ textXOffset = self.segmentWidth * idx + (self.segmentWidth - stringWidth) / 2.0f;
|
|
413
|
+
|
|
414
|
+ CGFloat whitespace = CGRectGetHeight(self.frame) - imageHeight - stringHeight - self.textImageSpacing;
|
|
415
|
+ if (self.imagePosition == HMSegmentedControlImagePositionAboveText) {
|
|
416
|
+ imageYOffset = ceilf(whitespace / 2.0);
|
|
417
|
+ textYOffset = imageYOffset + imageHeight + self.textImageSpacing;
|
|
418
|
+ } else if (self.imagePosition == HMSegmentedControlImagePositionBelowText) {
|
|
419
|
+ textYOffset = ceilf(whitespace / 2.0);
|
|
420
|
+ imageYOffset = textYOffset + stringHeight + self.textImageSpacing;
|
|
421
|
+ }
|
|
422
|
+ }
|
|
423
|
+ } else if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
|
|
424
|
+ // When we are drawing dynamic widths, we need to loop the widths array to calculate the xOffset
|
|
425
|
+ CGFloat xOffset = 0;
|
|
426
|
+ NSInteger i = 0;
|
|
427
|
+
|
|
428
|
+ for (NSNumber *width in self.segmentWidthsArray) {
|
|
429
|
+ if (idx == i) {
|
|
430
|
+ break;
|
|
431
|
+ }
|
|
432
|
+
|
|
433
|
+ xOffset = xOffset + [width floatValue];
|
|
434
|
+ i++;
|
|
435
|
+ }
|
|
436
|
+
|
|
437
|
+ BOOL isImageInLineWidthText = self.imagePosition == HMSegmentedControlImagePositionLeftOfText || self.imagePosition == HMSegmentedControlImagePositionRightOfText;
|
|
438
|
+ if (isImageInLineWidthText) {
|
|
439
|
+ if (self.imagePosition == HMSegmentedControlImagePositionLeftOfText) {
|
|
440
|
+ imageXOffset = xOffset;
|
|
441
|
+ textXOffset = imageXOffset + imageWidth + self.textImageSpacing;
|
|
442
|
+ } else {
|
|
443
|
+ textXOffset = xOffset;
|
|
444
|
+ imageXOffset = textXOffset + stringWidth + self.textImageSpacing;
|
|
445
|
+ }
|
|
446
|
+ } else {
|
|
447
|
+ imageXOffset = xOffset + ([self.segmentWidthsArray[i] floatValue] - imageWidth) / 2.0f; // Start with edge inset
|
|
448
|
+ textXOffset = xOffset + ([self.segmentWidthsArray[i] floatValue] - stringWidth) / 2.0f;
|
|
449
|
+
|
|
450
|
+ CGFloat whitespace = CGRectGetHeight(self.frame) - imageHeight - stringHeight - self.textImageSpacing;
|
|
451
|
+ if (self.imagePosition == HMSegmentedControlImagePositionAboveText) {
|
|
452
|
+ imageYOffset = ceilf(whitespace / 2.0);
|
|
453
|
+ textYOffset = imageYOffset + imageHeight + self.textImageSpacing;
|
|
454
|
+ } else if (self.imagePosition == HMSegmentedControlImagePositionBelowText) {
|
|
455
|
+ textYOffset = ceilf(whitespace / 2.0);
|
|
456
|
+ imageYOffset = textYOffset + stringHeight + self.textImageSpacing;
|
|
457
|
+ }
|
|
458
|
+ }
|
|
459
|
+ }
|
|
460
|
+
|
|
461
|
+ CGRect imageRect = CGRectMake(imageXOffset, imageYOffset, imageWidth, imageHeight);
|
|
462
|
+ CGRect textRect = CGRectMake(ceilf(textXOffset), ceilf(textYOffset), ceilf(stringWidth), ceilf(stringHeight));
|
|
463
|
+
|
|
464
|
+ CATextLayer *titleLayer = [CATextLayer layer];
|
|
465
|
+ titleLayer.frame = textRect;
|
|
466
|
+ titleLayer.alignmentMode = kCAAlignmentCenter;
|
|
467
|
+ titleLayer.string = [self attributedTitleAtIndex:idx];
|
|
468
|
+ if ([UIDevice currentDevice].systemVersion.floatValue < 10.0 ) {
|
|
469
|
+ titleLayer.truncationMode = kCATruncationEnd;
|
|
470
|
+ }
|
|
471
|
+ CALayer *imageLayer = [CALayer layer];
|
|
472
|
+ imageLayer.frame = imageRect;
|
|
473
|
+
|
|
474
|
+ if (self.selectedSegmentIndex == idx) {
|
|
475
|
+ if (self.sectionSelectedImages) {
|
|
476
|
+ UIImage *highlightIcon = [self.sectionSelectedImages objectAtIndex:idx];
|
|
477
|
+ imageLayer.contents = (id)highlightIcon.CGImage;
|
|
478
|
+ } else {
|
|
479
|
+ imageLayer.contents = (id)icon.CGImage;
|
|
480
|
+ }
|
|
481
|
+ } else {
|
|
482
|
+ imageLayer.contents = (id)icon.CGImage;
|
|
483
|
+ }
|
|
484
|
+
|
|
485
|
+ [self.scrollView.layer addSublayer:imageLayer];
|
|
486
|
+ titleLayer.contentsScale = [[UIScreen mainScreen] scale];
|
|
487
|
+ [self.scrollView.layer addSublayer:titleLayer];
|
|
488
|
+
|
|
489
|
+ [self addBackgroundAndBorderLayerWithRect:imageRect];
|
|
490
|
+ }];
|
|
491
|
+ }
|
|
492
|
+
|
|
493
|
+ // Add the selection indicators
|
|
494
|
+ if (self.selectedSegmentIndex != HMSegmentedControlNoSegment) {
|
|
495
|
+ if (self.selectionStyle == HMSegmentedControlSelectionStyleArrow) {
|
|
496
|
+ if (!self.selectionIndicatorArrowLayer.superlayer) {
|
|
497
|
+ [self setArrowFrame];
|
|
498
|
+ [self.scrollView.layer addSublayer:self.selectionIndicatorArrowLayer];
|
|
499
|
+ }
|
|
500
|
+ } else {
|
|
501
|
+ if (!self.selectionIndicatorStripLayer.superlayer) {
|
|
502
|
+ self.selectionIndicatorStripLayer.frame = [self frameForSelectionIndicator];
|
|
503
|
+ [self.scrollView.layer addSublayer:self.selectionIndicatorStripLayer];
|
|
504
|
+
|
|
505
|
+ if (self.selectionStyle == HMSegmentedControlSelectionStyleBox && !self.selectionIndicatorBoxLayer.superlayer) {
|
|
506
|
+ self.selectionIndicatorBoxLayer.frame = [self frameForFillerSelectionIndicator];
|
|
507
|
+ [self.scrollView.layer insertSublayer:self.selectionIndicatorBoxLayer atIndex:0];
|
|
508
|
+ }
|
|
509
|
+ }
|
|
510
|
+ }
|
|
511
|
+ }
|
|
512
|
+}
|
|
513
|
+
|
|
514
|
+- (void)addBackgroundAndBorderLayerWithRect:(CGRect)fullRect {
|
|
515
|
+ // Background layer
|
|
516
|
+ CALayer *backgroundLayer = [CALayer layer];
|
|
517
|
+ backgroundLayer.frame = fullRect;
|
|
518
|
+ [self.layer insertSublayer:backgroundLayer atIndex:0];
|
|
519
|
+
|
|
520
|
+ // Border layer
|
|
521
|
+ if (self.borderType & HMSegmentedControlBorderTypeTop) {
|
|
522
|
+ CALayer *borderLayer = [CALayer layer];
|
|
523
|
+ borderLayer.frame = CGRectMake(0, 0, fullRect.size.width, self.borderWidth);
|
|
524
|
+ borderLayer.backgroundColor = self.borderColor.CGColor;
|
|
525
|
+ [backgroundLayer addSublayer: borderLayer];
|
|
526
|
+ }
|
|
527
|
+ if (self.borderType & HMSegmentedControlBorderTypeLeft) {
|
|
528
|
+ CALayer *borderLayer = [CALayer layer];
|
|
529
|
+ borderLayer.frame = CGRectMake(0, 0, self.borderWidth, fullRect.size.height);
|
|
530
|
+ borderLayer.backgroundColor = self.borderColor.CGColor;
|
|
531
|
+ [backgroundLayer addSublayer: borderLayer];
|
|
532
|
+ }
|
|
533
|
+ if (self.borderType & HMSegmentedControlBorderTypeBottom) {
|
|
534
|
+ CALayer *borderLayer = [CALayer layer];
|
|
535
|
+ borderLayer.frame = CGRectMake(0, fullRect.size.height - self.borderWidth, fullRect.size.width, self.borderWidth);
|
|
536
|
+ borderLayer.backgroundColor = self.borderColor.CGColor;
|
|
537
|
+ [backgroundLayer addSublayer: borderLayer];
|
|
538
|
+ }
|
|
539
|
+ if (self.borderType & HMSegmentedControlBorderTypeRight) {
|
|
540
|
+ CALayer *borderLayer = [CALayer layer];
|
|
541
|
+ borderLayer.frame = CGRectMake(fullRect.size.width - self.borderWidth, 0, self.borderWidth, fullRect.size.height);
|
|
542
|
+ borderLayer.backgroundColor = self.borderColor.CGColor;
|
|
543
|
+ [backgroundLayer addSublayer: borderLayer];
|
|
544
|
+ }
|
|
545
|
+}
|
|
546
|
+
|
|
547
|
+- (void)setArrowFrame {
|
|
548
|
+ self.selectionIndicatorArrowLayer.frame = [self frameForSelectionIndicator];
|
|
549
|
+
|
|
550
|
+ self.selectionIndicatorArrowLayer.mask = nil;
|
|
551
|
+
|
|
552
|
+ UIBezierPath *arrowPath = [UIBezierPath bezierPath];
|
|
553
|
+
|
|
554
|
+ CGPoint p1 = CGPointZero;
|
|
555
|
+ CGPoint p2 = CGPointZero;
|
|
556
|
+ CGPoint p3 = CGPointZero;
|
|
557
|
+
|
|
558
|
+ if (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationDown) {
|
|
559
|
+ p1 = CGPointMake(self.selectionIndicatorArrowLayer.bounds.size.width / 2, 0);
|
|
560
|
+ p2 = CGPointMake(0, self.selectionIndicatorArrowLayer.bounds.size.height);
|
|
561
|
+ p3 = CGPointMake(self.selectionIndicatorArrowLayer.bounds.size.width, self.selectionIndicatorArrowLayer.bounds.size.height);
|
|
562
|
+ }
|
|
563
|
+
|
|
564
|
+ if (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationUp) {
|
|
565
|
+ p1 = CGPointMake(self.selectionIndicatorArrowLayer.bounds.size.width / 2, self.selectionIndicatorArrowLayer.bounds.size.height);
|
|
566
|
+ p2 = CGPointMake(self.selectionIndicatorArrowLayer.bounds.size.width, 0);
|
|
567
|
+ p3 = CGPointMake(0, 0);
|
|
568
|
+ }
|
|
569
|
+
|
|
570
|
+ [arrowPath moveToPoint:p1];
|
|
571
|
+ [arrowPath addLineToPoint:p2];
|
|
572
|
+ [arrowPath addLineToPoint:p3];
|
|
573
|
+ [arrowPath closePath];
|
|
574
|
+
|
|
575
|
+ CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
|
|
576
|
+ maskLayer.frame = self.selectionIndicatorArrowLayer.bounds;
|
|
577
|
+ maskLayer.path = arrowPath.CGPath;
|
|
578
|
+ self.selectionIndicatorArrowLayer.mask = maskLayer;
|
|
579
|
+}
|
|
580
|
+
|
|
581
|
+- (CGRect)frameForSelectionIndicator {
|
|
582
|
+ CGFloat indicatorYOffset = 0.0f;
|
|
583
|
+
|
|
584
|
+ if (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationDown) {
|
|
585
|
+ indicatorYOffset = self.bounds.size.height - self.selectionIndicatorHeight + self.selectionIndicatorEdgeInsets.bottom;
|
|
586
|
+ }
|
|
587
|
+
|
|
588
|
+ if (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationUp) {
|
|
589
|
+ indicatorYOffset = self.selectionIndicatorEdgeInsets.top;
|
|
590
|
+ }
|
|
591
|
+
|
|
592
|
+ CGFloat sectionWidth = 0.0f;
|
|
593
|
+
|
|
594
|
+ if (self.type == HMSegmentedControlTypeText) {
|
|
595
|
+ CGFloat stringWidth = [self measureTitleAtIndex:self.selectedSegmentIndex].width;
|
|
596
|
+ sectionWidth = stringWidth;
|
|
597
|
+ } else if (self.type == HMSegmentedControlTypeImages) {
|
|
598
|
+ UIImage *sectionImage = [self.sectionImages objectAtIndex:self.selectedSegmentIndex];
|
|
599
|
+ CGFloat imageWidth = sectionImage.size.width;
|
|
600
|
+ sectionWidth = imageWidth;
|
|
601
|
+ } else if (self.type == HMSegmentedControlTypeTextImages) {
|
|
602
|
+ CGFloat stringWidth = [self measureTitleAtIndex:self.selectedSegmentIndex].width;
|
|
603
|
+ UIImage *sectionImage = [self.sectionImages objectAtIndex:self.selectedSegmentIndex];
|
|
604
|
+ CGFloat imageWidth = sectionImage.size.width;
|
|
605
|
+ sectionWidth = MAX(stringWidth, imageWidth);
|
|
606
|
+ }
|
|
607
|
+
|
|
608
|
+ if (self.selectionStyle == HMSegmentedControlSelectionStyleArrow) {
|
|
609
|
+ CGFloat widthToEndOfSelectedSegment = (self.segmentWidth * self.selectedSegmentIndex) + self.segmentWidth;
|
|
610
|
+ CGFloat widthToStartOfSelectedIndex = (self.segmentWidth * self.selectedSegmentIndex);
|
|
611
|
+
|
|
612
|
+ CGFloat x = widthToStartOfSelectedIndex + ((widthToEndOfSelectedSegment - widthToStartOfSelectedIndex) / 2) - (self.selectionIndicatorHeight/2);
|
|
613
|
+ return CGRectMake(x - (self.selectionIndicatorHeight / 2), indicatorYOffset, self.selectionIndicatorHeight * 2, self.selectionIndicatorHeight);
|
|
614
|
+ } else {
|
|
615
|
+ if (self.selectionStyle == HMSegmentedControlSelectionStyleTextWidthStripe &&
|
|
616
|
+ sectionWidth <= self.segmentWidth &&
|
|
617
|
+ self.segmentWidthStyle != HMSegmentedControlSegmentWidthStyleDynamic) {
|
|
618
|
+ CGFloat widthToEndOfSelectedSegment = (self.segmentWidth * self.selectedSegmentIndex) + self.segmentWidth;
|
|
619
|
+ CGFloat widthToStartOfSelectedIndex = (self.segmentWidth * self.selectedSegmentIndex);
|
|
620
|
+
|
|
621
|
+ CGFloat x = ((widthToEndOfSelectedSegment - widthToStartOfSelectedIndex) / 2) + (widthToStartOfSelectedIndex - sectionWidth / 2);
|
|
622
|
+ return CGRectMake(x + self.selectionIndicatorEdgeInsets.left, indicatorYOffset, sectionWidth - self.selectionIndicatorEdgeInsets.right, self.selectionIndicatorHeight);
|
|
623
|
+ } else {
|
|
624
|
+ if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
|
|
625
|
+ CGFloat selectedSegmentOffset = 0.0f;
|
|
626
|
+
|
|
627
|
+ NSInteger i = 0;
|
|
628
|
+ for (NSNumber *width in self.segmentWidthsArray) {
|
|
629
|
+ if (self.selectedSegmentIndex == i)
|
|
630
|
+ break;
|
|
631
|
+ selectedSegmentOffset = selectedSegmentOffset + [width floatValue];
|
|
632
|
+ i++;
|
|
633
|
+ }
|
|
634
|
+ return CGRectMake(selectedSegmentOffset + self.selectionIndicatorEdgeInsets.left, indicatorYOffset, [[self.segmentWidthsArray objectAtIndex:self.selectedSegmentIndex] floatValue] - self.selectionIndicatorEdgeInsets.right, self.selectionIndicatorHeight + self.selectionIndicatorEdgeInsets.bottom);
|
|
635
|
+ }
|
|
636
|
+
|
|
637
|
+ return CGRectMake((self.segmentWidth + self.selectionIndicatorEdgeInsets.left) * self.selectedSegmentIndex, indicatorYOffset, self.segmentWidth - self.selectionIndicatorEdgeInsets.right, self.selectionIndicatorHeight);
|
|
638
|
+ }
|
|
639
|
+ }
|
|
640
|
+}
|
|
641
|
+
|
|
642
|
+- (CGRect)frameForFillerSelectionIndicator {
|
|
643
|
+ if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
|
|
644
|
+ CGFloat selectedSegmentOffset = 0.0f;
|
|
645
|
+
|
|
646
|
+ NSInteger i = 0;
|
|
647
|
+ for (NSNumber *width in self.segmentWidthsArray) {
|
|
648
|
+ if (self.selectedSegmentIndex == i) {
|
|
649
|
+ break;
|
|
650
|
+ }
|
|
651
|
+ selectedSegmentOffset = selectedSegmentOffset + [width floatValue];
|
|
652
|
+
|
|
653
|
+ i++;
|
|
654
|
+ }
|
|
655
|
+
|
|
656
|
+ return CGRectMake(selectedSegmentOffset, 0, [[self.segmentWidthsArray objectAtIndex:self.selectedSegmentIndex] floatValue], CGRectGetHeight(self.frame));
|
|
657
|
+ }
|
|
658
|
+ return CGRectMake(self.segmentWidth * self.selectedSegmentIndex, 0, self.segmentWidth, CGRectGetHeight(self.frame));
|
|
659
|
+}
|
|
660
|
+
|
|
661
|
+- (void)updateSegmentsRects {
|
|
662
|
+ self.scrollView.contentInset = UIEdgeInsetsZero;
|
|
663
|
+ self.scrollView.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), CGRectGetHeight(self.frame));
|
|
664
|
+
|
|
665
|
+ if ([self sectionCount] > 0) {
|
|
666
|
+ self.segmentWidth = self.frame.size.width / [self sectionCount];
|
|
667
|
+ }
|
|
668
|
+
|
|
669
|
+ if (self.type == HMSegmentedControlTypeText && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
|
|
670
|
+ [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx, BOOL *stop) {
|
|
671
|
+ CGFloat stringWidth = [self measureTitleAtIndex:idx].width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
|
|
672
|
+ self.segmentWidth = MAX(stringWidth, self.segmentWidth);
|
|
673
|
+ }];
|
|
674
|
+ } else if (self.type == HMSegmentedControlTypeText && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
|
|
675
|
+ NSMutableArray *mutableSegmentWidths = [NSMutableArray array];
|
|
676
|
+
|
|
677
|
+ [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx, BOOL *stop) {
|
|
678
|
+ CGFloat stringWidth = [self measureTitleAtIndex:idx].width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
|
|
679
|
+ [mutableSegmentWidths addObject:[NSNumber numberWithFloat:stringWidth]];
|
|
680
|
+ }];
|
|
681
|
+ self.segmentWidthsArray = [mutableSegmentWidths copy];
|
|
682
|
+ } else if (self.type == HMSegmentedControlTypeImages) {
|
|
683
|
+ for (UIImage *sectionImage in self.sectionImages) {
|
|
684
|
+ CGFloat imageWidth = sectionImage.size.width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
|
|
685
|
+ self.segmentWidth = MAX(imageWidth, self.segmentWidth);
|
|
686
|
+ }
|
|
687
|
+ } else if (self.type == HMSegmentedControlTypeTextImages && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed){
|
|
688
|
+ //lets just use the title.. we will assume it is wider then images...
|
|
689
|
+ [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx, BOOL *stop) {
|
|
690
|
+ CGFloat stringWidth = [self measureTitleAtIndex:idx].width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
|
|
691
|
+ self.segmentWidth = MAX(stringWidth, self.segmentWidth);
|
|
692
|
+ }];
|
|
693
|
+ } else if (self.type == HMSegmentedControlTypeTextImages && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
|
|
694
|
+ NSMutableArray *mutableSegmentWidths = [NSMutableArray array];
|
|
695
|
+ __block CGFloat totalWidth = 0.0;
|
|
696
|
+
|
|
697
|
+ int i = 0;
|
|
698
|
+ [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx, BOOL *stop) {
|
|
699
|
+ CGFloat stringWidth = [self measureTitleAtIndex:idx].width + self.segmentEdgeInset.right;
|
|
700
|
+ UIImage *sectionImage = [self.sectionImages objectAtIndex:i];
|
|
701
|
+ CGFloat imageWidth = sectionImage.size.width + self.segmentEdgeInset.left;
|
|
702
|
+
|
|
703
|
+ CGFloat combinedWidth = 0.0;
|
|
704
|
+ if (self.imagePosition == HMSegmentedControlImagePositionLeftOfText || self.imagePosition == HMSegmentedControlImagePositionRightOfText) {
|
|
705
|
+ combinedWidth = imageWidth + stringWidth + self.textImageSpacing;
|
|
706
|
+ } else {
|
|
707
|
+ combinedWidth = MAX(imageWidth, stringWidth);
|
|
708
|
+ }
|
|
709
|
+
|
|
710
|
+ totalWidth += combinedWidth;
|
|
711
|
+
|
|
712
|
+ [mutableSegmentWidths addObject:[NSNumber numberWithFloat:combinedWidth]];
|
|
713
|
+ }];
|
|
714
|
+
|
|
715
|
+ if (self.shouldStretchSegmentsToScreenSize && totalWidth < self.bounds.size.width) {
|
|
716
|
+ CGFloat whitespace = self.bounds.size.width - totalWidth;
|
|
717
|
+ CGFloat whitespaceForSegment = whitespace / [mutableSegmentWidths count];
|
|
718
|
+ [mutableSegmentWidths enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
|
719
|
+ CGFloat extendedWidth = whitespaceForSegment + [obj floatValue];
|
|
720
|
+ [mutableSegmentWidths replaceObjectAtIndex:idx withObject:[NSNumber numberWithFloat:extendedWidth]];
|
|
721
|
+ }];
|
|
722
|
+ }
|
|
723
|
+
|
|
724
|
+ self.segmentWidthsArray = [mutableSegmentWidths copy];
|
|
725
|
+ }
|
|
726
|
+
|
|
727
|
+ self.scrollView.scrollEnabled = self.isUserDraggable;
|
|
728
|
+ self.scrollView.contentSize = CGSizeMake([self totalSegmentedControlWidth], self.frame.size.height);
|
|
729
|
+}
|
|
730
|
+
|
|
731
|
+- (NSUInteger)sectionCount {
|
|
732
|
+ if (self.type == HMSegmentedControlTypeText) {
|
|
733
|
+ return self.sectionTitles.count;
|
|
734
|
+ } else if (self.type == HMSegmentedControlTypeImages ||
|
|
735
|
+ self.type == HMSegmentedControlTypeTextImages) {
|
|
736
|
+ return self.sectionImages.count;
|
|
737
|
+ }
|
|
738
|
+
|
|
739
|
+ return 0;
|
|
740
|
+}
|
|
741
|
+
|
|
742
|
+- (void)willMoveToSuperview:(UIView *)newSuperview {
|
|
743
|
+ // Control is being removed
|
|
744
|
+ if (newSuperview == nil)
|
|
745
|
+ return;
|
|
746
|
+
|
|
747
|
+ if (self.sectionTitles || self.sectionImages) {
|
|
748
|
+ [self updateSegmentsRects];
|
|
749
|
+ }
|
|
750
|
+}
|
|
751
|
+
|
|
752
|
+#pragma mark - Touch
|
|
753
|
+
|
|
754
|
+- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
|
|
755
|
+ UITouch *touch = [touches anyObject];
|
|
756
|
+ CGPoint touchLocation = [touch locationInView:self];
|
|
757
|
+
|
|
758
|
+ CGRect enlargeRect = CGRectMake(self.bounds.origin.x - self.enlargeEdgeInset.left,
|
|
759
|
+ self.bounds.origin.y - self.enlargeEdgeInset.top,
|
|
760
|
+ self.bounds.size.width + self.enlargeEdgeInset.left + self.enlargeEdgeInset.right,
|
|
761
|
+ self.bounds.size.height + self.enlargeEdgeInset.top + self.enlargeEdgeInset.bottom);
|
|
762
|
+
|
|
763
|
+ if (CGRectContainsPoint(enlargeRect, touchLocation)) {
|
|
764
|
+ NSInteger segment = 0;
|
|
765
|
+ if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
|
|
766
|
+ segment = (touchLocation.x + self.scrollView.contentOffset.x) / self.segmentWidth;
|
|
767
|
+ } else if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
|
|
768
|
+ // To know which segment the user touched, we need to loop over the widths and substract it from the x position.
|
|
769
|
+ CGFloat widthLeft = (touchLocation.x + self.scrollView.contentOffset.x);
|
|
770
|
+ for (NSNumber *width in self.segmentWidthsArray) {
|
|
771
|
+ widthLeft = widthLeft - [width floatValue];
|
|
772
|
+
|
|
773
|
+ // When we don't have any width left to substract, we have the segment index.
|
|
774
|
+ if (widthLeft <= 0)
|
|
775
|
+ break;
|
|
776
|
+
|
|
777
|
+ segment++;
|
|
778
|
+ }
|
|
779
|
+ }
|
|
780
|
+
|
|
781
|
+ NSUInteger sectionsCount = 0;
|
|
782
|
+
|
|
783
|
+ if (self.type == HMSegmentedControlTypeImages) {
|
|
784
|
+ sectionsCount = [self.sectionImages count];
|
|
785
|
+ } else if (self.type == HMSegmentedControlTypeTextImages || self.type == HMSegmentedControlTypeText) {
|
|
786
|
+ sectionsCount = [self.sectionTitles count];
|
|
787
|
+ }
|
|
788
|
+
|
|
789
|
+ if (segment != self.selectedSegmentIndex && segment < sectionsCount) {
|
|
790
|
+ // Check if we have to do anything with the touch event
|
|
791
|
+ if (self.isTouchEnabled)
|
|
792
|
+ [self setSelectedSegmentIndex:segment animated:self.shouldAnimateUserSelection notify:YES];
|
|
793
|
+ }
|
|
794
|
+ }
|
|
795
|
+}
|
|
796
|
+
|
|
797
|
+#pragma mark - Scrolling
|
|
798
|
+
|
|
799
|
+- (CGFloat)totalSegmentedControlWidth {
|
|
800
|
+ if (self.type == HMSegmentedControlTypeText && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
|
|
801
|
+ return self.sectionTitles.count * self.segmentWidth;
|
|
802
|
+ } else if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
|
|
803
|
+ return [[self.segmentWidthsArray valueForKeyPath:@"@sum.self"] floatValue];
|
|
804
|
+ } else {
|
|
805
|
+ return self.sectionImages.count * self.segmentWidth;
|
|
806
|
+ }
|
|
807
|
+}
|
|
808
|
+
|
|
809
|
+- (void)scrollToSelectedSegmentIndex:(BOOL)animated {
|
|
810
|
+ CGRect rectForSelectedIndex = CGRectZero;
|
|
811
|
+ CGFloat selectedSegmentOffset = 0;
|
|
812
|
+ if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
|
|
813
|
+ rectForSelectedIndex = CGRectMake(self.segmentWidth * self.selectedSegmentIndex,
|
|
814
|
+ 0,
|
|
815
|
+ self.segmentWidth,
|
|
816
|
+ self.frame.size.height);
|
|
817
|
+
|
|
818
|
+ selectedSegmentOffset = (CGRectGetWidth(self.frame) / 2) - (self.segmentWidth / 2);
|
|
819
|
+ } else {
|
|
820
|
+ NSInteger i = 0;
|
|
821
|
+ CGFloat offsetter = 0;
|
|
822
|
+ for (NSNumber *width in self.segmentWidthsArray) {
|
|
823
|
+ if (self.selectedSegmentIndex == i)
|
|
824
|
+ break;
|
|
825
|
+ offsetter = offsetter + [width floatValue];
|
|
826
|
+ i++;
|
|
827
|
+ }
|
|
828
|
+
|
|
829
|
+ rectForSelectedIndex = CGRectMake(offsetter,
|
|
830
|
+ 0,
|
|
831
|
+ [[self.segmentWidthsArray objectAtIndex:self.selectedSegmentIndex] floatValue],
|
|
832
|
+ self.frame.size.height);
|
|
833
|
+
|
|
834
|
+ selectedSegmentOffset = (CGRectGetWidth(self.frame) / 2) - ([[self.segmentWidthsArray objectAtIndex:self.selectedSegmentIndex] floatValue] / 2);
|
|
835
|
+ }
|
|
836
|
+
|
|
837
|
+
|
|
838
|
+ CGRect rectToScrollTo = rectForSelectedIndex;
|
|
839
|
+ rectToScrollTo.origin.x -= selectedSegmentOffset;
|
|
840
|
+ rectToScrollTo.size.width += selectedSegmentOffset * 2;
|
|
841
|
+ [self.scrollView scrollRectToVisible:rectToScrollTo animated:animated];
|
|
842
|
+}
|
|
843
|
+
|
|
844
|
+#pragma mark - Index Change
|
|
845
|
+
|
|
846
|
+- (void)setSelectedSegmentIndex:(NSInteger)index {
|
|
847
|
+ [self setSelectedSegmentIndex:index animated:NO notify:NO];
|
|
848
|
+}
|
|
849
|
+
|
|
850
|
+- (void)setSelectedSegmentIndex:(NSUInteger)index animated:(BOOL)animated {
|
|
851
|
+ [self setSelectedSegmentIndex:index animated:animated notify:NO];
|
|
852
|
+}
|
|
853
|
+
|
|
854
|
+- (void)setSelectedSegmentIndex:(NSUInteger)index animated:(BOOL)animated notify:(BOOL)notify {
|
|
855
|
+ _selectedSegmentIndex = index;
|
|
856
|
+ [self setNeedsDisplay];
|
|
857
|
+
|
|
858
|
+ if (index == HMSegmentedControlNoSegment) {
|
|
859
|
+ [self.selectionIndicatorArrowLayer removeFromSuperlayer];
|
|
860
|
+ [self.selectionIndicatorStripLayer removeFromSuperlayer];
|
|
861
|
+ [self.selectionIndicatorBoxLayer removeFromSuperlayer];
|
|
862
|
+ } else {
|
|
863
|
+ [self scrollToSelectedSegmentIndex:animated];
|
|
864
|
+
|
|
865
|
+ if (animated) {
|
|
866
|
+ // If the selected segment layer is not added to the super layer, that means no
|
|
867
|
+ // index is currently selected, so add the layer then move it to the new
|
|
868
|
+ // segment index without animating.
|
|
869
|
+ if(self.selectionStyle == HMSegmentedControlSelectionStyleArrow) {
|
|
870
|
+ if ([self.selectionIndicatorArrowLayer superlayer] == nil) {
|
|
871
|
+ [self.scrollView.layer addSublayer:self.selectionIndicatorArrowLayer];
|
|
872
|
+
|
|
873
|
+ [self setSelectedSegmentIndex:index animated:NO notify:YES];
|
|
874
|
+ return;
|
|
875
|
+ }
|
|
876
|
+ }else {
|
|
877
|
+ if ([self.selectionIndicatorStripLayer superlayer] == nil) {
|
|
878
|
+ [self.scrollView.layer addSublayer:self.selectionIndicatorStripLayer];
|
|
879
|
+
|
|
880
|
+ if (self.selectionStyle == HMSegmentedControlSelectionStyleBox && [self.selectionIndicatorBoxLayer superlayer] == nil)
|
|
881
|
+ [self.scrollView.layer insertSublayer:self.selectionIndicatorBoxLayer atIndex:0];
|
|
882
|
+
|
|
883
|
+ [self setSelectedSegmentIndex:index animated:NO notify:YES];
|
|
884
|
+ return;
|
|
885
|
+ }
|
|
886
|
+ }
|
|
887
|
+
|
|
888
|
+ if (notify)
|
|
889
|
+ [self notifyForSegmentChangeToIndex:index];
|
|
890
|
+
|
|
891
|
+ // Restore CALayer animations
|
|
892
|
+ self.selectionIndicatorArrowLayer.actions = nil;
|
|
893
|
+ self.selectionIndicatorStripLayer.actions = nil;
|
|
894
|
+ self.selectionIndicatorBoxLayer.actions = nil;
|
|
895
|
+
|
|
896
|
+ // Animate to new position
|
|
897
|
+ [CATransaction begin];
|
|
898
|
+ [CATransaction setAnimationDuration:0.15f];
|
|
899
|
+ [CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
|
|
900
|
+ [self setArrowFrame];
|
|
901
|
+ self.selectionIndicatorBoxLayer.frame = [self frameForSelectionIndicator];
|
|
902
|
+ self.selectionIndicatorStripLayer.frame = [self frameForSelectionIndicator];
|
|
903
|
+ self.selectionIndicatorBoxLayer.frame = [self frameForFillerSelectionIndicator];
|
|
904
|
+ [CATransaction commit];
|
|
905
|
+ } else {
|
|
906
|
+ // Disable CALayer animations
|
|
907
|
+ NSMutableDictionary *newActions = [[NSMutableDictionary alloc] initWithObjectsAndKeys:[NSNull null], @"position", [NSNull null], @"bounds", nil];
|
|
908
|
+ self.selectionIndicatorArrowLayer.actions = newActions;
|
|
909
|
+ [self setArrowFrame];
|
|
910
|
+
|
|
911
|
+ self.selectionIndicatorStripLayer.actions = newActions;
|
|
912
|
+ self.selectionIndicatorStripLayer.frame = [self frameForSelectionIndicator];
|
|
913
|
+
|
|
914
|
+ self.selectionIndicatorBoxLayer.actions = newActions;
|
|
915
|
+ self.selectionIndicatorBoxLayer.frame = [self frameForFillerSelectionIndicator];
|
|
916
|
+
|
|
917
|
+ if (notify)
|
|
918
|
+ [self notifyForSegmentChangeToIndex:index];
|
|
919
|
+ }
|
|
920
|
+ }
|
|
921
|
+}
|
|
922
|
+
|
|
923
|
+- (void)notifyForSegmentChangeToIndex:(NSInteger)index {
|
|
924
|
+ if (self.superview)
|
|
925
|
+ [self sendActionsForControlEvents:UIControlEventValueChanged];
|
|
926
|
+
|
|
927
|
+ if (self.indexChangeBlock)
|
|
928
|
+ self.indexChangeBlock(index);
|
|
929
|
+}
|
|
930
|
+
|
|
931
|
+#pragma mark - Styling Support
|
|
932
|
+
|
|
933
|
+- (NSDictionary *)resultingTitleTextAttributes {
|
|
934
|
+ NSDictionary *defaults = @{
|
|
935
|
+ NSFontAttributeName : [UIFont systemFontOfSize:19.0f],
|
|
936
|
+ NSForegroundColorAttributeName : [UIColor blackColor],
|
|
937
|
+ };
|
|
938
|
+
|
|
939
|
+ NSMutableDictionary *resultingAttrs = [NSMutableDictionary dictionaryWithDictionary:defaults];
|
|
940
|
+
|
|
941
|
+ if (self.titleTextAttributes) {
|
|
942
|
+ [resultingAttrs addEntriesFromDictionary:self.titleTextAttributes];
|
|
943
|
+ }
|
|
944
|
+
|
|
945
|
+ return [resultingAttrs copy];
|
|
946
|
+}
|
|
947
|
+
|
|
948
|
+- (NSDictionary *)resultingSelectedTitleTextAttributes {
|
|
949
|
+ NSMutableDictionary *resultingAttrs = [NSMutableDictionary dictionaryWithDictionary:[self resultingTitleTextAttributes]];
|
|
950
|
+
|
|
951
|
+ if (self.selectedTitleTextAttributes) {
|
|
952
|
+ [resultingAttrs addEntriesFromDictionary:self.selectedTitleTextAttributes];
|
|
953
|
+ }
|
|
954
|
+
|
|
955
|
+ return [resultingAttrs copy];
|
|
956
|
+}
|
|
957
|
+
|
|
958
|
+@end
|