HMSegmentedControl.m 52 KB

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