HMSegmentedControl.m 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784
  1. //
  2. // HMSegmentedControl.m
  3. // HMSegmentedControl
  4. //
  5. // Created by Hesham Abd-Elmegid on 23/12/12.
  6. // Copyright (c) 2012 Hesham Abd-Elmegid. All rights reserved.
  7. //
  8. #import "HMSegmentedControl.h"
  9. #import <QuartzCore/QuartzCore.h>
  10. #import <math.h>
  11. #define segmentImageTextPadding 7
  12. @interface HMScrollView : UIScrollView
  13. @end
  14. @interface HMSegmentedControl ()
  15. @property (nonatomic, strong) CALayer *selectionIndicatorStripLayer;
  16. @property (nonatomic, strong) CALayer *selectionIndicatorBoxLayer;
  17. @property (nonatomic, strong) CALayer *selectionIndicatorArrowLayer;
  18. @property (nonatomic, readwrite) CGFloat segmentWidth;
  19. @property (nonatomic, readwrite) NSArray *segmentWidthsArray;
  20. @property (nonatomic, strong) HMScrollView *scrollView;
  21. @end
  22. @implementation HMScrollView
  23. - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  24. if (!self.dragging) {
  25. [self.nextResponder touchesBegan:touches withEvent:event];
  26. } else {
  27. [super touchesBegan:touches withEvent:event];
  28. }
  29. }
  30. - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
  31. if (!self.dragging) {
  32. [self.nextResponder touchesMoved:touches withEvent:event];
  33. } else{
  34. [super touchesMoved:touches withEvent:event];
  35. }
  36. }
  37. - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
  38. if (!self.dragging) {
  39. [self.nextResponder touchesEnded:touches withEvent:event];
  40. } else {
  41. [super touchesEnded:touches withEvent:event];
  42. }
  43. }
  44. @end
  45. @implementation HMSegmentedControl
  46. - (id)initWithCoder:(NSCoder *)aDecoder {
  47. self = [super initWithCoder:aDecoder];
  48. if (self) {
  49. [self commonInit];
  50. }
  51. return self;
  52. }
  53. - (id)initWithFrame:(CGRect)frame {
  54. self = [super initWithFrame:frame];
  55. if (self) {
  56. _selectedSegmentIndex = 0;
  57. [self commonInit];
  58. }
  59. return self;
  60. }
  61. - (id)initWithSectionTitles:(NSArray *)sectiontitles {
  62. self = [self initWithFrame:CGRectZero];
  63. if (self) {
  64. [self commonInit];
  65. self.sectionTitles = sectiontitles;
  66. self.type = HMSegmentedControlTypeText;
  67. }
  68. return self;
  69. }
  70. - (id)initWithSectionImages:(NSArray*)sectionImages sectionSelectedImages:(NSArray*)sectionSelectedImages {
  71. self = [super initWithFrame:CGRectZero];
  72. if (self) {
  73. [self commonInit];
  74. self.sectionImages = sectionImages;
  75. self.sectionSelectedImages = sectionSelectedImages;
  76. self.type = HMSegmentedControlTypeImages;
  77. }
  78. return self;
  79. }
  80. - (instancetype)initWithSectionImages:(NSArray *)sectionImages sectionSelectedImages:(NSArray *)sectionSelectedImages titlesForSections:(NSArray *)sectiontitles {
  81. self = [super initWithFrame:CGRectZero];
  82. if (self) {
  83. [self commonInit];
  84. if (sectionImages.count != sectiontitles.count) {
  85. [NSException raise:NSRangeException format:@"***%s: Images bounds (%ld) Dont match Title bounds (%ld)", sel_getName(_cmd), (unsigned long)sectionImages.count, (unsigned long)sectiontitles.count];
  86. }
  87. self.sectionImages = sectionImages;
  88. self.sectionSelectedImages = sectionSelectedImages;
  89. self.sectionTitles = sectiontitles;
  90. self.type = HMSegmentedControlTypeTextImages;
  91. }
  92. return self;
  93. }
  94. - (void)awakeFromNib {
  95. [super awakeFromNib];
  96. self.segmentWidth = 0.0f;
  97. [self commonInit];
  98. }
  99. - (void)commonInit {
  100. self.scrollView = [[HMScrollView alloc] init];
  101. self.scrollView.scrollsToTop = NO;
  102. self.scrollView.showsVerticalScrollIndicator = NO;
  103. self.scrollView.showsHorizontalScrollIndicator = NO;
  104. [self addSubview:self.scrollView];
  105. self.font = [UIFont fontWithName:@"STHeitiSC-Light" size:18.0f];
  106. self.textColor = [UIColor blackColor];
  107. self.selectedTextColor = [UIColor blackColor];
  108. self.backgroundColor = [UIColor whiteColor];
  109. self.opaque = NO;
  110. self.selectionIndicatorColor = [UIColor colorWithRed:52.0f/255.0f green:181.0f/255.0f blue:229.0f/255.0f alpha:1.0f];
  111. self.selectedSegmentIndex = 0;
  112. self.segmentEdgeInset = UIEdgeInsetsMake(0, 5, 0, 5);
  113. self.selectionIndicatorHeight = 5.0f;
  114. self.selectionIndicatorEdgeInsets = UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f);
  115. self.selectionStyle = HMSegmentedControlSelectionStyleTextWidthStripe;
  116. self.selectionIndicatorLocation = HMSegmentedControlSelectionIndicatorLocationUp;
  117. self.segmentWidthStyle = HMSegmentedControlSegmentWidthStyleFixed;
  118. self.userDraggable = YES;
  119. self.touchEnabled = YES;
  120. self.type = HMSegmentedControlTypeText;
  121. self.shouldAnimateUserSelection = YES;
  122. self.selectionIndicatorArrowLayer = [CALayer layer];
  123. self.selectionIndicatorStripLayer = [CALayer layer];
  124. self.selectionIndicatorBoxLayer = [CALayer layer];
  125. self.selectionIndicatorBoxLayer.opacity = self.selectionIndicatorBoxOpacity;
  126. self.selectionIndicatorBoxLayer.borderWidth = 1.0f;
  127. self.selectionIndicatorBoxOpacity = 0.2;
  128. self.contentMode = UIViewContentModeRedraw;
  129. }
  130. - (void)layoutSubviews {
  131. [super layoutSubviews];
  132. [self updateSegmentsRects];
  133. }
  134. - (void)setFrame:(CGRect)frame {
  135. [super setFrame:frame];
  136. [self updateSegmentsRects];
  137. }
  138. - (void)setSectionTitles:(NSArray *)sectionTitles {
  139. _sectionTitles = sectionTitles;
  140. [self setNeedsLayout];
  141. }
  142. - (void)setSectionImages:(NSArray *)sectionImages {
  143. _sectionImages = sectionImages;
  144. [self setNeedsLayout];
  145. }
  146. - (void)setSelectionIndicatorLocation:(HMSegmentedControlSelectionIndicatorLocation)selectionIndicatorLocation {
  147. _selectionIndicatorLocation = selectionIndicatorLocation;
  148. if (selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationNone) {
  149. self.selectionIndicatorHeight = 0.0f;
  150. }
  151. }
  152. - (void)setSelectionIndicatorBoxOpacity:(CGFloat)selectionIndicatorBoxOpacity
  153. {
  154. _selectionIndicatorBoxOpacity = selectionIndicatorBoxOpacity;
  155. self.selectionIndicatorBoxLayer.opacity = _selectionIndicatorBoxOpacity;
  156. }
  157. #pragma mark - Drawing
  158. - (void)drawRect:(CGRect)rect {
  159. [self.backgroundColor setFill];
  160. UIRectFill([self bounds]);
  161. self.selectionIndicatorArrowLayer.backgroundColor = self.selectionIndicatorColor.CGColor;
  162. self.selectionIndicatorStripLayer.backgroundColor = self.selectionIndicatorColor.CGColor;
  163. self.selectionIndicatorBoxLayer.backgroundColor = self.selectionIndicatorColor.CGColor;
  164. self.selectionIndicatorBoxLayer.borderColor = self.selectionIndicatorColor.CGColor;
  165. // Remove all sublayers to avoid drawing images over existing ones
  166. self.scrollView.layer.sublayers = nil;
  167. if (self.type == HMSegmentedControlTypeText) {
  168. [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx, BOOL *stop) {
  169. CGFloat stringWidth = 0;
  170. CGFloat stringHeight = 0;
  171. if([titleString respondsToSelector:@selector(sizeWithAttributes:)]) {
  172. stringWidth = [titleString sizeWithAttributes:@{NSFontAttributeName: self.font}].width;
  173. stringHeight = [titleString sizeWithAttributes:@{NSFontAttributeName: self.font}].height;
  174. }
  175. else {
  176. stringWidth = roundf([titleString sizeWithFont:self.font].width);
  177. stringHeight = roundf([titleString sizeWithFont:self.font].height);
  178. }
  179. // Text inside the CATextLayer will appear blurry unless the rect values are rounded
  180. CGFloat y = roundf(CGRectGetHeight(self.frame) - self.selectionIndicatorHeight)/2 - stringHeight/2 + ((self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationUp) ? self.selectionIndicatorHeight : 0);
  181. CGRect rect;
  182. if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
  183. rect = CGRectMake((self.segmentWidth * idx) + (self.segmentWidth - stringWidth)/2, y, stringWidth, stringHeight);
  184. } else if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
  185. // When we are drawing dynamic widths, we need to loop the widths array to calculate the xOffset
  186. CGFloat xOffset = 0;
  187. NSInteger i = 0;
  188. for (NSNumber *width in self.segmentWidthsArray) {
  189. if (idx == i)
  190. break;
  191. xOffset = xOffset + [width floatValue];
  192. i++;
  193. }
  194. rect = CGRectMake(xOffset, y, [[self.segmentWidthsArray objectAtIndex:idx] floatValue], stringHeight);
  195. }
  196. CATextLayer *titleLayer = [CATextLayer layer];
  197. titleLayer.frame = rect;
  198. titleLayer.font = (__bridge CFTypeRef)(self.font.fontName);
  199. titleLayer.fontSize = self.font.pointSize;
  200. titleLayer.alignmentMode = kCAAlignmentCenter;
  201. titleLayer.string = titleString;
  202. titleLayer.truncationMode = kCATruncationEnd;
  203. if (self.selectedSegmentIndex == idx) {
  204. titleLayer.foregroundColor = self.selectedTextColor.CGColor;
  205. } else {
  206. titleLayer.foregroundColor = self.textColor.CGColor;
  207. }
  208. titleLayer.contentsScale = [[UIScreen mainScreen] scale];
  209. [self.scrollView.layer addSublayer:titleLayer];
  210. }];
  211. } else if (self.type == HMSegmentedControlTypeImages) {
  212. [self.sectionImages enumerateObjectsUsingBlock:^(id iconImage, NSUInteger idx, BOOL *stop) {
  213. UIImage *icon = iconImage;
  214. CGFloat imageWidth = icon.size.width;
  215. CGFloat imageHeight = icon.size.height;
  216. CGFloat y = roundf(CGRectGetHeight(self.frame) - self.selectionIndicatorHeight) / 2 - imageHeight / 2 + ((self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationUp) ? self.selectionIndicatorHeight : 0);
  217. CGFloat x = self.segmentWidth * idx + (self.segmentWidth - imageWidth)/2.0f;
  218. CGRect rect = CGRectMake(x, y, imageWidth, imageHeight);
  219. CALayer *imageLayer = [CALayer layer];
  220. imageLayer.frame = rect;
  221. if (self.selectedSegmentIndex == idx) {
  222. if (self.sectionSelectedImages) {
  223. UIImage *highlightIcon = [self.sectionSelectedImages objectAtIndex:idx];
  224. imageLayer.contents = (id)highlightIcon.CGImage;
  225. } else {
  226. imageLayer.contents = (id)icon.CGImage;
  227. }
  228. } else {
  229. imageLayer.contents = (id)icon.CGImage;
  230. }
  231. [self.scrollView.layer addSublayer:imageLayer];
  232. }];
  233. } else if (self.type == HMSegmentedControlTypeTextImages){
  234. [self.sectionImages enumerateObjectsUsingBlock:^(id iconImage, NSUInteger idx, BOOL *stop) {
  235. // When we have both an image and a title, we start with the image and use segmentImageTextPadding before drawing the text.
  236. // So the image will be left to the text, centered in the middle
  237. UIImage *icon = iconImage;
  238. CGFloat imageWidth = icon.size.width;
  239. CGFloat imageHeight = icon.size.height;
  240. CGFloat stringHeight = roundf([self.sectionTitles[idx] sizeWithFont:self.font].height);
  241. CGFloat yOffset = roundf(((CGRectGetHeight(self.frame) - self.selectionIndicatorHeight) / 2) - (stringHeight / 2));
  242. CGFloat imageXOffset = self.segmentEdgeInset.left; // Start with edge inset
  243. if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed)
  244. imageXOffset = self.segmentWidth * idx;
  245. else if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
  246. // When we are drawing dynamic widths, we need to loop the widths array to calculate the xOffset
  247. NSInteger i = 0;
  248. for (NSNumber *width in self.segmentWidthsArray) {
  249. if (idx == i)
  250. break;
  251. imageXOffset = imageXOffset + [width floatValue];
  252. i++;
  253. }
  254. }
  255. CGRect imageRect = CGRectMake(imageXOffset, yOffset, imageWidth, imageHeight);
  256. // Use the image offset and padding to calculate the text offset
  257. CGFloat textXOffset = imageXOffset + imageWidth + segmentImageTextPadding;
  258. // The text rect's width is the segment width without the image, image padding and insets
  259. CGRect textRect = CGRectMake(textXOffset, yOffset, [[self.segmentWidthsArray objectAtIndex:idx] floatValue]-imageWidth-segmentImageTextPadding-self.segmentEdgeInset.left-self.segmentEdgeInset.right, stringHeight);
  260. CATextLayer *titleLayer = [CATextLayer layer];
  261. titleLayer.frame = textRect;
  262. titleLayer.font = (__bridge CFTypeRef)(self.font.fontName);
  263. titleLayer.fontSize = self.font.pointSize;
  264. titleLayer.alignmentMode = kCAAlignmentCenter;
  265. titleLayer.string = self.sectionTitles[idx];
  266. titleLayer.truncationMode = kCATruncationEnd;
  267. CALayer *imageLayer = [CALayer layer];
  268. imageLayer.frame = imageRect;
  269. if (self.selectedSegmentIndex == idx) {
  270. if (self.sectionSelectedImages) {
  271. UIImage *highlightIcon = [self.sectionSelectedImages objectAtIndex:idx];
  272. imageLayer.contents = (id)highlightIcon.CGImage;
  273. } else {
  274. imageLayer.contents = (id)icon.CGImage;
  275. }
  276. titleLayer.foregroundColor = self.selectedTextColor.CGColor;
  277. } else {
  278. imageLayer.contents = (id)icon.CGImage;
  279. titleLayer.foregroundColor = self.textColor.CGColor;
  280. }
  281. [self.scrollView.layer addSublayer:imageLayer];
  282. titleLayer.contentsScale = [[UIScreen mainScreen] scale];
  283. [self.scrollView.layer addSublayer:titleLayer];
  284. }];
  285. }
  286. // Add the selection indicators
  287. if (self.selectedSegmentIndex != HMSegmentedControlNoSegment) {
  288. if (self.selectionStyle == HMSegmentedControlSelectionStyleArrow) {
  289. if (!self.selectionIndicatorArrowLayer.superlayer) {
  290. [self setArrowFrame];
  291. [self.scrollView.layer addSublayer:self.selectionIndicatorArrowLayer];
  292. }
  293. } else {
  294. if (!self.selectionIndicatorStripLayer.superlayer) {
  295. self.selectionIndicatorStripLayer.frame = [self frameForSelectionIndicator];
  296. [self.scrollView.layer addSublayer:self.selectionIndicatorStripLayer];
  297. if (self.selectionStyle == HMSegmentedControlSelectionStyleBox && !self.selectionIndicatorBoxLayer.superlayer) {
  298. self.selectionIndicatorBoxLayer.frame = [self frameForFillerSelectionIndicator];
  299. [self.scrollView.layer insertSublayer:self.selectionIndicatorBoxLayer atIndex:0];
  300. }
  301. }
  302. }
  303. }
  304. }
  305. - (void)setArrowFrame {
  306. self.selectionIndicatorArrowLayer.frame = [self frameForSelectionIndicator];
  307. self.selectionIndicatorArrowLayer.mask = nil;
  308. UIBezierPath *arrowPath = [UIBezierPath bezierPath];
  309. CGPoint p1 = CGPointZero;
  310. CGPoint p2 = CGPointZero;
  311. CGPoint p3 = CGPointZero;
  312. if (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationDown) {
  313. p1 = CGPointMake(self.selectionIndicatorArrowLayer.bounds.size.width / 2, 0);
  314. p2 = CGPointMake(0, self.selectionIndicatorArrowLayer.bounds.size.height);
  315. p3 = CGPointMake(self.selectionIndicatorArrowLayer.bounds.size.width, self.selectionIndicatorArrowLayer.bounds.size.height);
  316. }
  317. if (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationUp) {
  318. p1 = CGPointMake(self.selectionIndicatorArrowLayer.bounds.size.width / 2, self.selectionIndicatorArrowLayer.bounds.size.height);
  319. p2 = CGPointMake(self.selectionIndicatorArrowLayer.bounds.size.width, 0);
  320. p3 = CGPointMake(0, 0);
  321. }
  322. [arrowPath moveToPoint:p1];
  323. [arrowPath addLineToPoint:p2];
  324. [arrowPath addLineToPoint:p3];
  325. [arrowPath closePath];
  326. CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
  327. maskLayer.frame = self.selectionIndicatorArrowLayer.bounds;
  328. maskLayer.path = arrowPath.CGPath;
  329. self.selectionIndicatorArrowLayer.mask = maskLayer;
  330. }
  331. - (CGRect)frameForSelectionIndicator {
  332. CGFloat indicatorYOffset = 0.0f;
  333. // if (_selectedSegmentIndex < 0) {
  334. // _selectedSegmentIndex = 0;
  335. // }
  336. if (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationDown) {
  337. indicatorYOffset = self.bounds.size.height - self.selectionIndicatorHeight + self.selectionIndicatorEdgeInsets.bottom;
  338. }
  339. if (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationUp) {
  340. indicatorYOffset = self.selectionIndicatorEdgeInsets.top;
  341. }
  342. CGFloat sectionWidth = 0.0f;
  343. if (self.type == HMSegmentedControlTypeText) {
  344. // if (_sectionImages.count) {
  345. CGFloat stringWidth = [[_sectionTitles objectAtIndex:_selectedSegmentIndex] sizeWithFont:self.font].width;
  346. sectionWidth = stringWidth;
  347. // }
  348. // else{
  349. // CGFloat stringWidth = [@"待付款" sizeWithFont:self.font].width;
  350. // sectionWidth = stringWidth;
  351. //
  352. //
  353. // }
  354. } else if (self.type == HMSegmentedControlTypeImages) {
  355. UIImage *sectionImage = [self.sectionImages objectAtIndex:self.selectedSegmentIndex];
  356. CGFloat imageWidth = sectionImage.size.width;
  357. sectionWidth = imageWidth;
  358. } else if (self.type == HMSegmentedControlTypeTextImages) {
  359. CGFloat stringWidth = [[self.sectionTitles objectAtIndex:self.selectedSegmentIndex] sizeWithFont:self.font].width;
  360. UIImage *sectionImage = [self.sectionImages objectAtIndex:self.selectedSegmentIndex];
  361. CGFloat imageWidth = sectionImage.size.width;
  362. if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
  363. sectionWidth = MAX(stringWidth, imageWidth);
  364. } else if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
  365. sectionWidth = imageWidth + segmentImageTextPadding + stringWidth;
  366. }
  367. }
  368. if (self.selectionStyle == HMSegmentedControlSelectionStyleArrow) {
  369. CGFloat widthToEndOfSelectedSegment = (self.segmentWidth * self.selectedSegmentIndex) + self.segmentWidth;
  370. CGFloat widthToStartOfSelectedIndex = (self.segmentWidth * self.selectedSegmentIndex);
  371. CGFloat x = widthToStartOfSelectedIndex + ((widthToEndOfSelectedSegment - widthToStartOfSelectedIndex) / 2) - (self.selectionIndicatorHeight/2);
  372. return CGRectMake(x, indicatorYOffset, self.selectionIndicatorHeight, self.selectionIndicatorHeight);
  373. } else {
  374. if (self.selectionStyle == HMSegmentedControlSelectionStyleTextWidthStripe &&
  375. sectionWidth <= self.segmentWidth &&
  376. self.segmentWidthStyle != HMSegmentedControlSegmentWidthStyleDynamic) {
  377. CGFloat widthToEndOfSelectedSegment = (self.segmentWidth * self.selectedSegmentIndex) + self.segmentWidth;
  378. CGFloat widthToStartOfSelectedIndex = (self.segmentWidth * self.selectedSegmentIndex);
  379. CGFloat x = ((widthToEndOfSelectedSegment - widthToStartOfSelectedIndex) / 2) + (widthToStartOfSelectedIndex - sectionWidth / 2);
  380. return CGRectMake(x + self.selectionIndicatorEdgeInsets.left, indicatorYOffset, sectionWidth - self.selectionIndicatorEdgeInsets.right, self.selectionIndicatorHeight);
  381. } else {
  382. if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
  383. CGFloat selectedSegmentOffset = 0.0f;
  384. NSInteger i = 0;
  385. for (NSNumber *width in self.segmentWidthsArray) {
  386. if (self.selectedSegmentIndex == i)
  387. break;
  388. selectedSegmentOffset = selectedSegmentOffset + [width floatValue];
  389. i++;
  390. }
  391. return CGRectMake(selectedSegmentOffset + self.selectionIndicatorEdgeInsets.left, indicatorYOffset, [[self.segmentWidthsArray objectAtIndex:self.selectedSegmentIndex] floatValue] - self.selectionIndicatorEdgeInsets.right, self.selectionIndicatorHeight + self.selectionIndicatorEdgeInsets.bottom);
  392. }
  393. return CGRectMake((self.segmentWidth + self.selectionIndicatorEdgeInsets.left) * self.selectedSegmentIndex, indicatorYOffset, self.segmentWidth - self.selectionIndicatorEdgeInsets.right, self.selectionIndicatorHeight);
  394. }
  395. }
  396. }
  397. - (CGRect)frameForFillerSelectionIndicator {
  398. if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
  399. CGFloat selectedSegmentOffset = 0.0f;
  400. NSInteger i = 0;
  401. for (NSNumber *width in self.segmentWidthsArray) {
  402. if (self.selectedSegmentIndex == i) {
  403. break;
  404. }
  405. selectedSegmentOffset = selectedSegmentOffset + [width floatValue];
  406. i++;
  407. }
  408. return CGRectMake(selectedSegmentOffset, 0, [[self.segmentWidthsArray objectAtIndex:self.selectedSegmentIndex] floatValue], CGRectGetHeight(self.frame));
  409. }
  410. return CGRectMake(self.segmentWidth * self.selectedSegmentIndex, 0, self.segmentWidth, CGRectGetHeight(self.frame));
  411. }
  412. - (void)updateSegmentsRects {
  413. self.scrollView.contentInset = UIEdgeInsetsZero;
  414. self.scrollView.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), CGRectGetHeight(self.frame));
  415. // When `scrollEnabled` is set to YES, segment width will be automatically set to the width of the biggest segment's text or image,
  416. // otherwise it will be equal to the width of the control's frame divided by the number of segments.
  417. if ([self sectionCount] > 0) {
  418. self.segmentWidth = self.frame.size.width / [self sectionCount];
  419. }
  420. if (self.type == HMSegmentedControlTypeText && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
  421. for (NSString *titleString in self.sectionTitles) {
  422. #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000
  423. CGFloat stringWidth = [titleString sizeWithAttributes:@{NSFontAttributeName: self.font}].width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
  424. #else
  425. CGFloat stringWidth = [titleString sizeWithFont:self.font].width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
  426. #endif
  427. self.segmentWidth = MAX(stringWidth, self.segmentWidth);
  428. }
  429. } else if (self.type == HMSegmentedControlTypeText && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
  430. NSMutableArray *mutableSegmentWidths = [NSMutableArray array];
  431. for (NSString *titleString in self.sectionTitles) {
  432. #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000
  433. CGFloat stringWidth = [titleString sizeWithAttributes:@{NSFontAttributeName: self.font}].width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
  434. #else
  435. CGFloat stringWidth = [titleString sizeWithFont:self.font].width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
  436. #endif
  437. [mutableSegmentWidths addObject:[NSNumber numberWithFloat:stringWidth]];
  438. }
  439. self.segmentWidthsArray = [mutableSegmentWidths copy];
  440. } else if (self.type == HMSegmentedControlTypeImages) {
  441. for (UIImage *sectionImage in self.sectionImages) {
  442. CGFloat imageWidth = sectionImage.size.width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
  443. self.segmentWidth = MAX(imageWidth, self.segmentWidth);
  444. }
  445. } else if (self.type == HMSegmentedControlTypeTextImages && HMSegmentedControlSegmentWidthStyleFixed){
  446. //lets just use the title.. we will assume it is wider then images...
  447. for (NSString *titleString in self.sectionTitles) {
  448. #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000
  449. CGFloat stringWidth = [titleString sizeWithAttributes:@{NSFontAttributeName: self.font}].width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
  450. #else
  451. CGFloat stringWidth = [titleString sizeWithFont:self.font].width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
  452. #endif
  453. self.segmentWidth = MAX(stringWidth, self.segmentWidth);
  454. }
  455. } else if (self.type == HMSegmentedControlTypeTextImages && HMSegmentedControlSegmentWidthStyleDynamic) {
  456. NSMutableArray *mutableSegmentWidths = [NSMutableArray array];
  457. int i = 0;
  458. for (NSString *titleString in self.sectionTitles) {
  459. #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000
  460. CGFloat stringWidth = [titleString sizeWithAttributes:@{NSFontAttributeName: self.font}].width + self.segmentEdgeInset.right;
  461. #else
  462. CGFloat stringWidth = [titleString sizeWithFont:self.font].width + self.segmentEdgeInset.right;
  463. #endif
  464. UIImage *sectionImage = [self.sectionImages objectAtIndex:i];
  465. CGFloat imageWidth = sectionImage.size.width + self.segmentEdgeInset.left;
  466. CGFloat combinedWidth = imageWidth + segmentImageTextPadding + stringWidth;
  467. [mutableSegmentWidths addObject:[NSNumber numberWithFloat:combinedWidth]];
  468. i++;
  469. }
  470. self.segmentWidthsArray = [mutableSegmentWidths copy];
  471. }
  472. self.scrollView.scrollEnabled = self.isUserDraggable;
  473. self.scrollView.contentSize = CGSizeMake([self totalSegmentedControlWidth], self.frame.size.height);
  474. }
  475. - (NSUInteger)sectionCount {
  476. if (self.type == HMSegmentedControlTypeText) {
  477. return self.sectionTitles.count;
  478. } else if (self.type == HMSegmentedControlTypeImages ||
  479. self.type == HMSegmentedControlTypeTextImages) {
  480. return self.sectionImages.count;
  481. }
  482. return 0;
  483. }
  484. - (void)willMoveToSuperview:(UIView *)newSuperview {
  485. // Control is being removed
  486. if (newSuperview == nil)
  487. return;
  488. if (self.sectionTitles || self.sectionImages) {
  489. [self updateSegmentsRects];
  490. }
  491. }
  492. #pragma mark - Touch
  493. - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
  494. UITouch *touch = [touches anyObject];
  495. CGPoint touchLocation = [touch locationInView:self];
  496. if (CGRectContainsPoint(self.bounds, touchLocation)) {
  497. NSInteger segment = 0;
  498. if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
  499. segment = (touchLocation.x + self.scrollView.contentOffset.x) / self.segmentWidth;
  500. } else if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
  501. // To know which segment the user touched, we need to loop over the widths and substract it from the x position.
  502. CGFloat widthLeft = (touchLocation.x + self.scrollView.contentOffset.x);
  503. for (NSNumber *width in self.segmentWidthsArray) {
  504. widthLeft = widthLeft - [width floatValue];
  505. // When we don't have any width left to substract, we have the segment index.
  506. if (widthLeft <= 0)
  507. break;
  508. segment++;
  509. }
  510. }
  511. NSUInteger sectionsCount;
  512. if (self.type == HMSegmentedControlTypeImages) {
  513. sectionsCount = [self.sectionImages count];
  514. } else if (self.type == HMSegmentedControlTypeTextImages || self.type == HMSegmentedControlTypeText) {
  515. sectionsCount = [self.sectionTitles count];
  516. }
  517. if (segment != self.selectedSegmentIndex && segment < sectionsCount) {
  518. // Check if we have to do anything with the touch event
  519. if (self.isTouchEnabled)
  520. [self setSelectedSegmentIndex:segment animated:self.shouldAnimateUserSelection notify:YES];
  521. }
  522. }
  523. }
  524. #pragma mark - Scrolling
  525. - (CGFloat)totalSegmentedControlWidth {
  526. if (self.type == HMSegmentedControlTypeText && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
  527. return self.sectionTitles.count * self.segmentWidth;
  528. } else if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
  529. return [[self.segmentWidthsArray valueForKeyPath:@"@sum.self"] floatValue];
  530. } else {
  531. return self.sectionImages.count * self.segmentWidth;
  532. }
  533. }
  534. - (void)scrollToSelectedSegmentIndex {
  535. CGRect rectForSelectedIndex;
  536. CGFloat selectedSegmentOffset = 0;
  537. if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
  538. rectForSelectedIndex = CGRectMake(self.segmentWidth * self.selectedSegmentIndex,
  539. 0,
  540. self.segmentWidth,
  541. self.frame.size.height);
  542. selectedSegmentOffset = (CGRectGetWidth(self.frame) / 2) - (self.segmentWidth / 2);
  543. } else if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
  544. NSInteger i = 0;
  545. CGFloat offsetter = 0;
  546. for (NSNumber *width in self.segmentWidthsArray) {
  547. if (self.selectedSegmentIndex == i)
  548. break;
  549. offsetter = offsetter + [width floatValue];
  550. i++;
  551. }
  552. rectForSelectedIndex = CGRectMake(offsetter,
  553. 0,
  554. [[self.segmentWidthsArray objectAtIndex:self.selectedSegmentIndex] floatValue],
  555. self.frame.size.height);
  556. selectedSegmentOffset = (CGRectGetWidth(self.frame) / 2) - ([[self.segmentWidthsArray objectAtIndex:self.selectedSegmentIndex] floatValue] / 2);
  557. }
  558. CGRect rectToScrollTo = rectForSelectedIndex;
  559. rectToScrollTo.origin.x -= selectedSegmentOffset;
  560. rectToScrollTo.size.width += selectedSegmentOffset * 2;
  561. [self.scrollView scrollRectToVisible:rectToScrollTo animated:YES];
  562. }
  563. #pragma mark - Index change
  564. - (void)setSelectedSegmentIndex:(NSInteger)index {
  565. [self setSelectedSegmentIndex:index animated:NO notify:NO];
  566. }
  567. - (void)setSelectedSegmentIndex:(NSUInteger)index animated:(BOOL)animated {
  568. [self setSelectedSegmentIndex:index animated:animated notify:NO];
  569. }
  570. - (void)setSelectedSegmentIndex:(NSUInteger)index animated:(BOOL)animated notify:(BOOL)notify {
  571. _selectedSegmentIndex = index;
  572. [self setNeedsDisplay];
  573. if (index == HMSegmentedControlNoSegment) {
  574. [self.selectionIndicatorArrowLayer removeFromSuperlayer];
  575. [self.selectionIndicatorStripLayer removeFromSuperlayer];
  576. [self.selectionIndicatorBoxLayer removeFromSuperlayer];
  577. } else {
  578. [self scrollToSelectedSegmentIndex];
  579. if (animated) {
  580. // If the selected segment layer is not added to the super layer, that means no
  581. // index is currently selected, so add the layer then move it to the new
  582. // segment index without animating.
  583. if(self.selectionStyle == HMSegmentedControlSelectionStyleArrow) {
  584. if ([self.selectionIndicatorArrowLayer superlayer] == nil) {
  585. [self.scrollView.layer addSublayer:self.selectionIndicatorArrowLayer];
  586. [self setSelectedSegmentIndex:index animated:NO notify:YES];
  587. return;
  588. }
  589. }else {
  590. if ([self.selectionIndicatorStripLayer superlayer] == nil) {
  591. [self.scrollView.layer addSublayer:self.selectionIndicatorStripLayer];
  592. if (self.selectionStyle == HMSegmentedControlSelectionStyleBox && [self.selectionIndicatorBoxLayer superlayer] == nil)
  593. [self.scrollView.layer insertSublayer:self.selectionIndicatorBoxLayer atIndex:0];
  594. [self setSelectedSegmentIndex:index animated:NO notify:YES];
  595. return;
  596. }
  597. }
  598. if (notify)
  599. [self notifyForSegmentChangeToIndex:index];
  600. // Restore CALayer animations
  601. self.selectionIndicatorArrowLayer.actions = nil;
  602. self.selectionIndicatorStripLayer.actions = nil;
  603. self.selectionIndicatorBoxLayer.actions = nil;
  604. // Animate to new position
  605. [CATransaction begin];
  606. [CATransaction setAnimationDuration:0.15f];
  607. [CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
  608. [self setArrowFrame];
  609. self.selectionIndicatorBoxLayer.frame = [self frameForSelectionIndicator];
  610. self.selectionIndicatorStripLayer.frame = [self frameForSelectionIndicator];
  611. self.selectionIndicatorBoxLayer.frame = [self frameForFillerSelectionIndicator];
  612. [CATransaction commit];
  613. } else {
  614. // Disable CALayer animations
  615. NSMutableDictionary *newActions = [[NSMutableDictionary alloc] initWithObjectsAndKeys:[NSNull null], @"position", [NSNull null], @"bounds", nil];
  616. self.selectionIndicatorArrowLayer.actions = newActions;
  617. [self setArrowFrame];
  618. self.selectionIndicatorStripLayer.actions = newActions;
  619. self.selectionIndicatorStripLayer.frame = [self frameForSelectionIndicator];
  620. self.selectionIndicatorBoxLayer.actions = newActions;
  621. self.selectionIndicatorBoxLayer.frame = [self frameForFillerSelectionIndicator];
  622. if (notify)
  623. [self notifyForSegmentChangeToIndex:index];
  624. }
  625. }
  626. }
  627. - (void)notifyForSegmentChangeToIndex:(NSInteger)index {
  628. if (self.superview)
  629. [self sendActionsForControlEvents:UIControlEventValueChanged];
  630. if (self.indexChangeBlock)
  631. self.indexChangeBlock(index);
  632. }
  633. @end