HMSegmentedControl.m 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915
  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. if (!self.dragging) {
  24. [self.nextResponder touchesBegan:touches withEvent:event];
  25. } else {
  26. [super touchesBegan:touches withEvent:event];
  27. }
  28. }
  29. - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
  30. if (!self.dragging) {
  31. [self.nextResponder touchesMoved:touches withEvent:event];
  32. } else{
  33. [super touchesMoved:touches withEvent:event];
  34. }
  35. }
  36. - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  37. if (!self.dragging) {
  38. [self.nextResponder touchesEnded:touches withEvent:event];
  39. } else {
  40. [super touchesEnded:touches withEvent:event];
  41. }
  42. }
  43. @end
  44. @implementation HMSegmentedControl
  45. - (id)initWithCoder:(NSCoder *)aDecoder {
  46. self = [super initWithCoder:aDecoder];
  47. if (self) {
  48. [self commonInit];
  49. }
  50. return self;
  51. }
  52. - (id)initWithFrame:(CGRect)frame {
  53. self = [super initWithFrame:frame];
  54. if (self) {
  55. [self commonInit];
  56. }
  57. return self;
  58. }
  59. - (id)initWithSectionTitles:(NSArray<NSString *> *)sectiontitles {
  60. self = [super initWithFrame:CGRectZero];
  61. if (self) {
  62. [self commonInit];
  63. self.sectionTitles = sectiontitles;
  64. self.type = HMSegmentedControlTypeText;
  65. }
  66. return self;
  67. }
  68. - (id)initWithSectionImages:(NSArray<UIImage *> *)sectionImages sectionSelectedImages:(NSArray<UIImage *> *)sectionSelectedImages {
  69. self = [super initWithFrame:CGRectZero];
  70. if (self) {
  71. [self commonInit];
  72. self.sectionImages = sectionImages;
  73. self.sectionSelectedImages = sectionSelectedImages;
  74. self.type = HMSegmentedControlTypeImages;
  75. }
  76. return self;
  77. }
  78. - (instancetype)initWithSectionImages:(NSArray<UIImage *> *)sectionImages sectionSelectedImages:(NSArray<UIImage *> *)sectionSelectedImages titlesForSections:(NSArray<NSString *> *)sectiontitles {
  79. self = [super initWithFrame:CGRectZero];
  80. if (self) {
  81. [self commonInit];
  82. if (sectionImages.count != sectiontitles.count) {
  83. [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];
  84. }
  85. self.sectionImages = sectionImages;
  86. self.sectionSelectedImages = sectionSelectedImages;
  87. self.sectionTitles = sectiontitles;
  88. self.type = HMSegmentedControlTypeTextImages;
  89. }
  90. return self;
  91. }
  92. - (void)awakeFromNib {
  93. [super awakeFromNib];
  94. self.segmentWidth = 0.0f;
  95. }
  96. - (void)commonInit {
  97. self.scrollView = [[HMScrollView alloc] init];
  98. self.scrollView.scrollsToTop = NO;
  99. self.scrollView.showsVerticalScrollIndicator = NO;
  100. self.scrollView.showsHorizontalScrollIndicator = NO;
  101. [self addSubview:self.scrollView];
  102. _backgroundColor = [UIColor whiteColor];
  103. self.opaque = NO;
  104. _selectionIndicatorColor = [UIColor colorWithRed:52.0f/255.0f green:181.0f/255.0f blue:229.0f/255.0f alpha:1.0f];
  105. _selectionIndicatorBoxColor = _selectionIndicatorColor;
  106. self.selectedSegmentIndex = 0;
  107. self.segmentEdgeInset = UIEdgeInsetsMake(0, 5, 0, 5);
  108. self.selectionIndicatorHeight = 5.0f;
  109. self.selectionIndicatorEdgeInsets = UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f);
  110. self.selectionStyle = HMSegmentedControlSelectionStyleTextWidthStripe;
  111. self.selectionIndicatorLocation = HMSegmentedControlSelectionIndicatorLocationUp;
  112. self.segmentWidthStyle = HMSegmentedControlSegmentWidthStyleFixed;
  113. self.userDraggable = YES;
  114. self.touchEnabled = YES;
  115. self.verticalDividerEnabled = NO;
  116. self.type = HMSegmentedControlTypeText;
  117. self.verticalDividerWidth = 1.0f;
  118. _verticalDividerColor = [UIColor blackColor];
  119. self.borderColor = [UIColor blackColor];
  120. self.borderWidth = 1.0f;
  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<NSString *> *)sectionTitles {
  139. _sectionTitles = sectionTitles;
  140. [self setNeedsLayout];
  141. [self setNeedsDisplay];
  142. }
  143. - (void)setSectionImages:(NSArray<UIImage *> *)sectionImages {
  144. _sectionImages = sectionImages;
  145. [self setNeedsLayout];
  146. [self setNeedsDisplay];
  147. }
  148. - (void)setSelectionIndicatorLocation:(HMSegmentedControlSelectionIndicatorLocation)selectionIndicatorLocation {
  149. _selectionIndicatorLocation = selectionIndicatorLocation;
  150. if (selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationNone) {
  151. self.selectionIndicatorHeight = 0.0f;
  152. }
  153. }
  154. - (void)setSelectionIndicatorBoxOpacity:(CGFloat)selectionIndicatorBoxOpacity {
  155. _selectionIndicatorBoxOpacity = selectionIndicatorBoxOpacity;
  156. self.selectionIndicatorBoxLayer.opacity = _selectionIndicatorBoxOpacity;
  157. }
  158. - (void)setSegmentWidthStyle:(HMSegmentedControlSegmentWidthStyle)segmentWidthStyle {
  159. // Force HMSegmentedControlSegmentWidthStyleFixed when type is HMSegmentedControlTypeImages.
  160. if (self.type == HMSegmentedControlTypeImages) {
  161. _segmentWidthStyle = HMSegmentedControlSegmentWidthStyleFixed;
  162. } else {
  163. _segmentWidthStyle = segmentWidthStyle;
  164. }
  165. }
  166. - (void)setBorderType:(HMSegmentedControlBorderType)borderType {
  167. _borderType = borderType;
  168. [self setNeedsDisplay];
  169. }
  170. #pragma mark - Drawing
  171. - (CGSize)measureTitleAtIndex:(NSUInteger)index {
  172. if (index >= self.sectionTitles.count) {
  173. return CGSizeZero;
  174. }
  175. id title = self.sectionTitles[index];
  176. CGSize size = CGSizeZero;
  177. BOOL selected = (index == self.selectedSegmentIndex) ? YES : NO;
  178. if ([title isKindOfClass:[NSString class]] && !self.titleFormatter) {
  179. NSDictionary *titleAttrs = selected ? [self resultingSelectedTitleTextAttributes] : [self resultingTitleTextAttributes];
  180. size = [(NSString *)title sizeWithAttributes:titleAttrs];
  181. } else if ([title isKindOfClass:[NSString class]] && self.titleFormatter) {
  182. size = [self.titleFormatter(self, title, index, selected) size];
  183. } else if ([title isKindOfClass:[NSAttributedString class]]) {
  184. size = [(NSAttributedString *)title size];
  185. } else {
  186. NSAssert(title == nil, @"Unexpected type of segment title: %@", [title class]);
  187. size = CGSizeZero;
  188. }
  189. return CGRectIntegral((CGRect){CGPointZero, size}).size;
  190. }
  191. - (NSAttributedString *)attributedTitleAtIndex:(NSUInteger)index {
  192. id title = self.sectionTitles[index];
  193. BOOL selected = (index == self.selectedSegmentIndex) ? YES : NO;
  194. if ([title isKindOfClass:[NSAttributedString class]]) {
  195. return (NSAttributedString *)title;
  196. } else if (!self.titleFormatter) {
  197. NSDictionary *titleAttrs = selected ? [self resultingSelectedTitleTextAttributes] : [self resultingTitleTextAttributes];
  198. // the color should be cast to CGColor in order to avoid invalid context on iOS7
  199. UIColor *titleColor = titleAttrs[NSForegroundColorAttributeName];
  200. if (titleColor) {
  201. NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:titleAttrs];
  202. dict[NSForegroundColorAttributeName] = (id)titleColor.CGColor;
  203. titleAttrs = [NSDictionary dictionaryWithDictionary:dict];
  204. }
  205. return [[NSAttributedString alloc] initWithString:(NSString *)title attributes:titleAttrs];
  206. } else {
  207. return self.titleFormatter(self, title, index, selected);
  208. }
  209. }
  210. - (void)drawRect:(CGRect)rect {
  211. [self.backgroundColor setFill];
  212. UIRectFill([self bounds]);
  213. self.selectionIndicatorArrowLayer.backgroundColor = self.selectionIndicatorColor.CGColor;
  214. self.selectionIndicatorStripLayer.backgroundColor = self.selectionIndicatorColor.CGColor;
  215. self.selectionIndicatorBoxLayer.backgroundColor = self.selectionIndicatorBoxColor.CGColor;
  216. self.selectionIndicatorBoxLayer.borderColor = self.selectionIndicatorBoxColor.CGColor;
  217. // Remove all sublayers to avoid drawing images over existing ones
  218. self.scrollView.layer.sublayers = nil;
  219. CGRect oldRect = rect;
  220. if (self.type == HMSegmentedControlTypeText) {
  221. [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx, BOOL *stop) {
  222. CGFloat stringWidth = 0;
  223. CGFloat stringHeight = 0;
  224. CGSize size = [self measureTitleAtIndex:idx];
  225. stringWidth = size.width;
  226. stringHeight = size.height;
  227. CGRect rectDiv = CGRectZero;
  228. CGRect fullRect = CGRectZero;
  229. // Text inside the CATextLayer will appear blurry unless the rect values are rounded
  230. BOOL locationUp = (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationUp);
  231. BOOL selectionStyleNotBox = (self.selectionStyle != HMSegmentedControlSelectionStyleBox);
  232. CGFloat y = roundf((CGRectGetHeight(self.frame) - selectionStyleNotBox * self.selectionIndicatorHeight) / 2 - stringHeight / 2 + self.selectionIndicatorHeight * locationUp);
  233. CGRect rect;
  234. if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
  235. rect = CGRectMake((self.segmentWidth * idx) + (self.segmentWidth - stringWidth) / 2, y, stringWidth, stringHeight);
  236. rectDiv = CGRectMake((self.segmentWidth * idx) - (self.verticalDividerWidth / 2), self.selectionIndicatorHeight * 2, self.verticalDividerWidth, self.frame.size.height - (self.selectionIndicatorHeight * 4));
  237. fullRect = CGRectMake(self.segmentWidth * idx, 0, self.segmentWidth, oldRect.size.height);
  238. } else {
  239. // When we are drawing dynamic widths, we need to loop the widths array to calculate the xOffset
  240. CGFloat xOffset = 0;
  241. NSInteger i = 0;
  242. for (NSNumber *width in self.segmentWidthsArray) {
  243. if (idx == i)
  244. break;
  245. xOffset = xOffset + [width floatValue];
  246. i++;
  247. }
  248. CGFloat widthForIndex = [[self.segmentWidthsArray objectAtIndex:idx] floatValue];
  249. rect = CGRectMake(xOffset, y, widthForIndex, stringHeight);
  250. fullRect = CGRectMake(self.segmentWidth * idx, 0, widthForIndex, oldRect.size.height);
  251. rectDiv = CGRectMake(xOffset - (self.verticalDividerWidth / 2), self.selectionIndicatorHeight * 2, self.verticalDividerWidth, self.frame.size.height - (self.selectionIndicatorHeight * 4));
  252. }
  253. // Fix rect position/size to avoid blurry labels
  254. rect = CGRectMake(ceilf(rect.origin.x), ceilf(rect.origin.y), ceilf(rect.size.width), ceilf(rect.size.height));
  255. CATextLayer *titleLayer = [CATextLayer layer];
  256. titleLayer.frame = rect;
  257. titleLayer.alignmentMode = kCAAlignmentCenter;
  258. if ([UIDevice currentDevice].systemVersion.floatValue < 10.0 ) {
  259. titleLayer.truncationMode = kCATruncationEnd;
  260. }
  261. titleLayer.string = [self attributedTitleAtIndex:idx];
  262. titleLayer.contentsScale = [[UIScreen mainScreen] scale];
  263. [self.scrollView.layer addSublayer:titleLayer];
  264. // Vertical Divider
  265. if (self.isVerticalDividerEnabled && idx > 0) {
  266. CALayer *verticalDividerLayer = [CALayer layer];
  267. verticalDividerLayer.frame = rectDiv;
  268. verticalDividerLayer.backgroundColor = self.verticalDividerColor.CGColor;
  269. [self.scrollView.layer addSublayer:verticalDividerLayer];
  270. }
  271. [self addBackgroundAndBorderLayerWithRect:fullRect];
  272. }];
  273. } else if (self.type == HMSegmentedControlTypeImages) {
  274. [self.sectionImages enumerateObjectsUsingBlock:^(id iconImage, NSUInteger idx, BOOL *stop) {
  275. UIImage *icon = iconImage;
  276. CGFloat imageWidth = icon.size.width;
  277. CGFloat imageHeight = icon.size.height;
  278. CGFloat y = roundf(CGRectGetHeight(self.frame) - self.selectionIndicatorHeight) / 2 - imageHeight / 2 + ((self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationUp) ? self.selectionIndicatorHeight : 0);
  279. CGFloat x = self.segmentWidth * idx + (self.segmentWidth - imageWidth)/2.0f;
  280. CGRect rect = CGRectMake(x, y, imageWidth, imageHeight);
  281. CALayer *imageLayer = [CALayer layer];
  282. imageLayer.frame = rect;
  283. if (self.selectedSegmentIndex == idx) {
  284. if (self.sectionSelectedImages) {
  285. UIImage *highlightIcon = [self.sectionSelectedImages objectAtIndex:idx];
  286. imageLayer.contents = (id)highlightIcon.CGImage;
  287. } else {
  288. imageLayer.contents = (id)icon.CGImage;
  289. }
  290. } else {
  291. imageLayer.contents = (id)icon.CGImage;
  292. }
  293. [self.scrollView.layer addSublayer:imageLayer];
  294. // Vertical Divider
  295. if (self.isVerticalDividerEnabled && idx>0) {
  296. CALayer *verticalDividerLayer = [CALayer layer];
  297. verticalDividerLayer.frame = CGRectMake((self.segmentWidth * idx) - (self.verticalDividerWidth / 2), self.selectionIndicatorHeight * 2, self.verticalDividerWidth, self.frame.size.height-(self.selectionIndicatorHeight * 4));
  298. verticalDividerLayer.backgroundColor = self.verticalDividerColor.CGColor;
  299. [self.scrollView.layer addSublayer:verticalDividerLayer];
  300. }
  301. [self addBackgroundAndBorderLayerWithRect:rect];
  302. }];
  303. } else if (self.type == HMSegmentedControlTypeTextImages){
  304. [self.sectionImages enumerateObjectsUsingBlock:^(id iconImage, NSUInteger idx, BOOL *stop) {
  305. UIImage *icon = iconImage;
  306. CGFloat imageWidth = icon.size.width;
  307. CGFloat imageHeight = icon.size.height;
  308. CGFloat stringHeight = [self measureTitleAtIndex:idx].height;
  309. CGFloat yOffset = roundf(((CGRectGetHeight(self.frame) - self.selectionIndicatorHeight) / 2) - (stringHeight / 2));
  310. CGFloat imageXOffset = self.segmentEdgeInset.left; // Start with edge inset
  311. CGFloat textXOffset = self.segmentEdgeInset.left;
  312. CGFloat textWidth = 0;
  313. if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
  314. imageXOffset = (self.segmentWidth * idx) + (self.segmentWidth / 2.0f) - (imageWidth / 2.0f);
  315. textXOffset = self.segmentWidth * idx;
  316. textWidth = self.segmentWidth;
  317. } else if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
  318. // When we are drawing dynamic widths, we need to loop the widths array to calculate the xOffset
  319. CGFloat xOffset = 0;
  320. NSInteger i = 0;
  321. for (NSNumber *width in self.segmentWidthsArray) {
  322. if (idx == i) {
  323. break;
  324. }
  325. xOffset = xOffset + [width floatValue];
  326. i++;
  327. }
  328. imageXOffset = xOffset + ([self.segmentWidthsArray[idx] floatValue] / 2.0f) - (imageWidth / 2.0f); //(self.segmentWidth / 2.0f) - (imageWidth / 2.0f)
  329. textXOffset = xOffset;
  330. textWidth = [self.segmentWidthsArray[idx] floatValue];
  331. }
  332. CGFloat imageYOffset = roundf((CGRectGetHeight(self.frame) - self.selectionIndicatorHeight) / 2.0f);
  333. CGRect imageRect = CGRectMake(imageXOffset, imageYOffset, imageWidth, imageHeight);
  334. CGRect textRect = CGRectMake(textXOffset, yOffset, textWidth, stringHeight);
  335. // Fix rect position/size to avoid blurry labels
  336. textRect = CGRectMake(ceilf(textRect.origin.x), ceilf(textRect.origin.y), ceilf(textRect.size.width), ceilf(textRect.size.height));
  337. CATextLayer *titleLayer = [CATextLayer layer];
  338. titleLayer.frame = textRect;
  339. titleLayer.alignmentMode = kCAAlignmentCenter;
  340. titleLayer.string = [self attributedTitleAtIndex:idx];
  341. if ([UIDevice currentDevice].systemVersion.floatValue < 10.0 ) {
  342. titleLayer.truncationMode = kCATruncationEnd;
  343. }
  344. CALayer *imageLayer = [CALayer layer];
  345. imageLayer.frame = imageRect;
  346. if (self.selectedSegmentIndex == idx) {
  347. if (self.sectionSelectedImages) {
  348. UIImage *highlightIcon = [self.sectionSelectedImages objectAtIndex:idx];
  349. imageLayer.contents = (id)highlightIcon.CGImage;
  350. } else {
  351. imageLayer.contents = (id)icon.CGImage;
  352. }
  353. } else {
  354. imageLayer.contents = (id)icon.CGImage;
  355. }
  356. [self.scrollView.layer addSublayer:imageLayer];
  357. titleLayer.contentsScale = [[UIScreen mainScreen] scale];
  358. [self.scrollView.layer addSublayer:titleLayer];
  359. [self addBackgroundAndBorderLayerWithRect:imageRect];
  360. }];
  361. }
  362. // Add the selection indicators
  363. if (self.selectedSegmentIndex != HMSegmentedControlNoSegment) {
  364. if (self.selectionStyle == HMSegmentedControlSelectionStyleArrow) {
  365. if (!self.selectionIndicatorArrowLayer.superlayer) {
  366. [self setArrowFrame];
  367. [self.scrollView.layer addSublayer:self.selectionIndicatorArrowLayer];
  368. }
  369. } else {
  370. if (!self.selectionIndicatorStripLayer.superlayer) {
  371. self.selectionIndicatorStripLayer.frame = [self frameForSelectionIndicator];
  372. //rk- 7.28
  373. self.selectionIndicatorStripLayer.masksToBounds = YES;
  374. self.selectionIndicatorStripLayer.cornerRadius = self.selectionIndicatorStripLayer.frame.size.height/2;
  375. [self.scrollView.layer addSublayer:self.selectionIndicatorStripLayer];
  376. if (self.selectionStyle == HMSegmentedControlSelectionStyleBox && !self.selectionIndicatorBoxLayer.superlayer) {
  377. self.selectionIndicatorBoxLayer.frame = [self frameForFillerSelectionIndicator];
  378. [self.scrollView.layer insertSublayer:self.selectionIndicatorBoxLayer atIndex:0];
  379. }
  380. }
  381. }
  382. }
  383. }
  384. - (void)addBackgroundAndBorderLayerWithRect:(CGRect)fullRect {
  385. // Background layer
  386. CALayer *backgroundLayer = [CALayer layer];
  387. backgroundLayer.frame = fullRect;
  388. [self.layer insertSublayer:backgroundLayer atIndex:0];
  389. // Border layer
  390. if (self.borderType & HMSegmentedControlBorderTypeTop) {
  391. CALayer *borderLayer = [CALayer layer];
  392. borderLayer.frame = CGRectMake(0, 0, fullRect.size.width, self.borderWidth);
  393. borderLayer.backgroundColor = self.borderColor.CGColor;
  394. [backgroundLayer addSublayer: borderLayer];
  395. }
  396. if (self.borderType & HMSegmentedControlBorderTypeLeft) {
  397. CALayer *borderLayer = [CALayer layer];
  398. borderLayer.frame = CGRectMake(0, 0, self.borderWidth, fullRect.size.height);
  399. borderLayer.backgroundColor = self.borderColor.CGColor;
  400. [backgroundLayer addSublayer: borderLayer];
  401. }
  402. if (self.borderType & HMSegmentedControlBorderTypeBottom) {
  403. CALayer *borderLayer = [CALayer layer];
  404. borderLayer.frame = CGRectMake(0, fullRect.size.height - self.borderWidth, fullRect.size.width, self.borderWidth);
  405. borderLayer.backgroundColor = self.borderColor.CGColor;
  406. [backgroundLayer addSublayer: borderLayer];
  407. }
  408. if (self.borderType & HMSegmentedControlBorderTypeRight) {
  409. CALayer *borderLayer = [CALayer layer];
  410. borderLayer.frame = CGRectMake(fullRect.size.width - self.borderWidth, 0, self.borderWidth, fullRect.size.height);
  411. borderLayer.backgroundColor = self.borderColor.CGColor;
  412. [backgroundLayer addSublayer: borderLayer];
  413. }
  414. }
  415. - (void)setArrowFrame {
  416. self.selectionIndicatorArrowLayer.frame = [self frameForSelectionIndicator];
  417. self.selectionIndicatorArrowLayer.mask = nil;
  418. UIBezierPath *arrowPath = [UIBezierPath bezierPath];
  419. CGPoint p1 = CGPointZero;
  420. CGPoint p2 = CGPointZero;
  421. CGPoint p3 = CGPointZero;
  422. if (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationDown) {
  423. p1 = CGPointMake(self.selectionIndicatorArrowLayer.bounds.size.width / 2, 0);
  424. p2 = CGPointMake(0, self.selectionIndicatorArrowLayer.bounds.size.height);
  425. p3 = CGPointMake(self.selectionIndicatorArrowLayer.bounds.size.width, self.selectionIndicatorArrowLayer.bounds.size.height);
  426. }
  427. if (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationUp) {
  428. p1 = CGPointMake(self.selectionIndicatorArrowLayer.bounds.size.width / 2, self.selectionIndicatorArrowLayer.bounds.size.height);
  429. p2 = CGPointMake(self.selectionIndicatorArrowLayer.bounds.size.width, 0);
  430. p3 = CGPointMake(0, 0);
  431. }
  432. [arrowPath moveToPoint:p1];
  433. [arrowPath addLineToPoint:p2];
  434. [arrowPath addLineToPoint:p3];
  435. [arrowPath closePath];
  436. CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
  437. maskLayer.frame = self.selectionIndicatorArrowLayer.bounds;
  438. maskLayer.path = arrowPath.CGPath;
  439. self.selectionIndicatorArrowLayer.mask = maskLayer;
  440. }
  441. - (CGRect)frameForSelectionIndicator {
  442. CGFloat indicatorYOffset = 0.0f;
  443. if (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationDown) {
  444. indicatorYOffset = self.bounds.size.height - self.selectionIndicatorHeight + self.selectionIndicatorEdgeInsets.bottom;
  445. }
  446. if (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationUp) {
  447. indicatorYOffset = self.selectionIndicatorEdgeInsets.top;
  448. }
  449. CGFloat sectionWidth = 0.0f;
  450. if (self.type == HMSegmentedControlTypeText) {
  451. CGFloat stringWidth = [self measureTitleAtIndex:self.selectedSegmentIndex].width;
  452. sectionWidth = stringWidth;
  453. } else if (self.type == HMSegmentedControlTypeImages) {
  454. UIImage *sectionImage = [self.sectionImages objectAtIndex:self.selectedSegmentIndex];
  455. CGFloat imageWidth = sectionImage.size.width;
  456. sectionWidth = imageWidth;
  457. } else if (self.type == HMSegmentedControlTypeTextImages) {
  458. CGFloat stringWidth = [self measureTitleAtIndex:self.selectedSegmentIndex].width;
  459. UIImage *sectionImage = [self.sectionImages objectAtIndex:self.selectedSegmentIndex];
  460. CGFloat imageWidth = sectionImage.size.width;
  461. sectionWidth = MAX(stringWidth, imageWidth);
  462. }
  463. if (self.selectionStyle == HMSegmentedControlSelectionStyleArrow) {
  464. CGFloat widthToEndOfSelectedSegment = (self.segmentWidth * self.selectedSegmentIndex) + self.segmentWidth;
  465. CGFloat widthToStartOfSelectedIndex = (self.segmentWidth * self.selectedSegmentIndex);
  466. CGFloat x = widthToStartOfSelectedIndex + ((widthToEndOfSelectedSegment - widthToStartOfSelectedIndex) / 2) - (self.selectionIndicatorHeight/2);
  467. return CGRectMake(x - (self.selectionIndicatorHeight / 2), indicatorYOffset, self.selectionIndicatorHeight * 2, self.selectionIndicatorHeight);
  468. } else {
  469. if (self.selectionStyle == HMSegmentedControlSelectionStyleTextWidthStripe &&
  470. sectionWidth <= self.segmentWidth &&
  471. self.segmentWidthStyle != HMSegmentedControlSegmentWidthStyleDynamic) {
  472. CGFloat widthToEndOfSelectedSegment = (self.segmentWidth * self.selectedSegmentIndex) + self.segmentWidth;
  473. CGFloat widthToStartOfSelectedIndex = (self.segmentWidth * self.selectedSegmentIndex);
  474. CGFloat x = ((widthToEndOfSelectedSegment - widthToStartOfSelectedIndex) / 2) + (widthToStartOfSelectedIndex - sectionWidth / 2);
  475. return CGRectMake(x + self.selectionIndicatorEdgeInsets.left, indicatorYOffset, sectionWidth - self.selectionIndicatorEdgeInsets.right, self.selectionIndicatorHeight);
  476. } else {
  477. if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
  478. CGFloat selectedSegmentOffset = 0.0f;
  479. NSInteger i = 0;
  480. for (NSNumber *width in self.segmentWidthsArray) {
  481. if (self.selectedSegmentIndex == i)
  482. break;
  483. selectedSegmentOffset = selectedSegmentOffset + [width floatValue];
  484. i++;
  485. }
  486. return CGRectMake(selectedSegmentOffset + self.selectionIndicatorEdgeInsets.left, indicatorYOffset, [[self.segmentWidthsArray objectAtIndex:self.selectedSegmentIndex] floatValue] - self.selectionIndicatorEdgeInsets.right, self.selectionIndicatorHeight + self.selectionIndicatorEdgeInsets.bottom);
  487. }
  488. return CGRectMake((self.segmentWidth + self.selectionIndicatorEdgeInsets.left) * self.selectedSegmentIndex, indicatorYOffset, self.segmentWidth - self.selectionIndicatorEdgeInsets.right, self.selectionIndicatorHeight);
  489. }
  490. }
  491. }
  492. - (CGRect)frameForFillerSelectionIndicator {
  493. if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
  494. CGFloat selectedSegmentOffset = 0.0f;
  495. NSInteger i = 0;
  496. for (NSNumber *width in self.segmentWidthsArray) {
  497. if (self.selectedSegmentIndex == i) {
  498. break;
  499. }
  500. selectedSegmentOffset = selectedSegmentOffset + [width floatValue];
  501. i++;
  502. }
  503. return CGRectMake(selectedSegmentOffset, 0, [[self.segmentWidthsArray objectAtIndex:self.selectedSegmentIndex] floatValue], CGRectGetHeight(self.frame));
  504. }
  505. return CGRectMake(self.segmentWidth * self.selectedSegmentIndex, 0, self.segmentWidth, CGRectGetHeight(self.frame));
  506. }
  507. - (void)updateSegmentsRects {
  508. self.scrollView.contentInset = UIEdgeInsetsZero;
  509. self.scrollView.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), CGRectGetHeight(self.frame));
  510. if ([self sectionCount] > 0) {
  511. self.segmentWidth = self.frame.size.width / [self sectionCount];
  512. }
  513. if (self.type == HMSegmentedControlTypeText && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
  514. [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx, BOOL *stop) {
  515. CGFloat stringWidth = [self measureTitleAtIndex:idx].width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
  516. self.segmentWidth = MAX(stringWidth, self.segmentWidth);
  517. }];
  518. } else if (self.type == HMSegmentedControlTypeText && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
  519. NSMutableArray *mutableSegmentWidths = [NSMutableArray array];
  520. [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx, BOOL *stop) {
  521. CGFloat stringWidth = [self measureTitleAtIndex:idx].width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
  522. [mutableSegmentWidths addObject:[NSNumber numberWithFloat:stringWidth]];
  523. }];
  524. self.segmentWidthsArray = [mutableSegmentWidths copy];
  525. } else if (self.type == HMSegmentedControlTypeImages) {
  526. for (UIImage *sectionImage in self.sectionImages) {
  527. CGFloat imageWidth = sectionImage.size.width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
  528. self.segmentWidth = MAX(imageWidth, self.segmentWidth);
  529. }
  530. } else if (self.type == HMSegmentedControlTypeTextImages && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed){
  531. //lets just use the title.. we will assume it is wider then images...
  532. [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx, BOOL *stop) {
  533. CGFloat stringWidth = [self measureTitleAtIndex:idx].width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
  534. self.segmentWidth = MAX(stringWidth, self.segmentWidth);
  535. }];
  536. } else if (self.type == HMSegmentedControlTypeTextImages && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
  537. NSMutableArray *mutableSegmentWidths = [NSMutableArray array];
  538. int i = 0;
  539. [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx, BOOL *stop) {
  540. CGFloat stringWidth = [self measureTitleAtIndex:idx].width + self.segmentEdgeInset.right;
  541. UIImage *sectionImage = [self.sectionImages objectAtIndex:i];
  542. CGFloat imageWidth = sectionImage.size.width + self.segmentEdgeInset.left;
  543. CGFloat combinedWidth = MAX(imageWidth, stringWidth);
  544. [mutableSegmentWidths addObject:[NSNumber numberWithFloat:combinedWidth]];
  545. }];
  546. self.segmentWidthsArray = [mutableSegmentWidths copy];
  547. }
  548. self.scrollView.scrollEnabled = self.isUserDraggable;
  549. self.scrollView.contentSize = CGSizeMake([self totalSegmentedControlWidth], self.frame.size.height);
  550. }
  551. - (NSUInteger)sectionCount {
  552. if (self.type == HMSegmentedControlTypeText) {
  553. return self.sectionTitles.count;
  554. } else if (self.type == HMSegmentedControlTypeImages ||
  555. self.type == HMSegmentedControlTypeTextImages) {
  556. return self.sectionImages.count;
  557. }
  558. return 0;
  559. }
  560. - (void)willMoveToSuperview:(UIView *)newSuperview {
  561. // Control is being removed
  562. if (newSuperview == nil)
  563. return;
  564. if (self.sectionTitles || self.sectionImages) {
  565. [self updateSegmentsRects];
  566. }
  567. }
  568. #pragma mark - Touch
  569. - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  570. UITouch *touch = [touches anyObject];
  571. CGPoint touchLocation = [touch locationInView:self];
  572. CGRect enlargeRect = CGRectMake(self.bounds.origin.x - self.enlargeEdgeInset.left,
  573. self.bounds.origin.y - self.enlargeEdgeInset.top,
  574. self.bounds.size.width + self.enlargeEdgeInset.left + self.enlargeEdgeInset.right,
  575. self.bounds.size.height + self.enlargeEdgeInset.top + self.enlargeEdgeInset.bottom);
  576. if (CGRectContainsPoint(enlargeRect, touchLocation)) {
  577. NSInteger segment = 0;
  578. if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
  579. segment = (touchLocation.x + self.scrollView.contentOffset.x) / self.segmentWidth;
  580. } else if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
  581. // To know which segment the user touched, we need to loop over the widths and substract it from the x position.
  582. CGFloat widthLeft = (touchLocation.x + self.scrollView.contentOffset.x);
  583. for (NSNumber *width in self.segmentWidthsArray) {
  584. widthLeft = widthLeft - [width floatValue];
  585. // When we don't have any width left to substract, we have the segment index.
  586. if (widthLeft <= 0)
  587. break;
  588. segment++;
  589. }
  590. }
  591. NSUInteger sectionsCount = 0;
  592. if (self.type == HMSegmentedControlTypeImages) {
  593. sectionsCount = [self.sectionImages count];
  594. } else if (self.type == HMSegmentedControlTypeTextImages || self.type == HMSegmentedControlTypeText) {
  595. sectionsCount = [self.sectionTitles count];
  596. }
  597. if (segment != self.selectedSegmentIndex && segment < sectionsCount) {
  598. // Check if we have to do anything with the touch event
  599. if (self.isTouchEnabled)
  600. [self setSelectedSegmentIndex:segment animated:self.shouldAnimateUserSelection notify:YES];
  601. }
  602. }
  603. }
  604. #pragma mark - Scrolling
  605. - (CGFloat)totalSegmentedControlWidth {
  606. if (self.type == HMSegmentedControlTypeText && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
  607. return self.sectionTitles.count * self.segmentWidth;
  608. } else if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
  609. return [[self.segmentWidthsArray valueForKeyPath:@"@sum.self"] floatValue];
  610. } else {
  611. return self.sectionImages.count * self.segmentWidth;
  612. }
  613. }
  614. - (void)scrollToSelectedSegmentIndex:(BOOL)animated {
  615. CGRect rectForSelectedIndex = CGRectZero;
  616. CGFloat selectedSegmentOffset = 0;
  617. if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
  618. rectForSelectedIndex = CGRectMake(self.segmentWidth * self.selectedSegmentIndex,
  619. 0,
  620. self.segmentWidth,
  621. self.frame.size.height);
  622. selectedSegmentOffset = (CGRectGetWidth(self.frame) / 2) - (self.segmentWidth / 2);
  623. } else {
  624. NSInteger i = 0;
  625. CGFloat offsetter = 0;
  626. for (NSNumber *width in self.segmentWidthsArray) {
  627. if (self.selectedSegmentIndex == i)
  628. break;
  629. offsetter = offsetter + [width floatValue];
  630. i++;
  631. }
  632. rectForSelectedIndex = CGRectMake(offsetter,
  633. 0,
  634. [[self.segmentWidthsArray objectAtIndex:self.selectedSegmentIndex] floatValue],
  635. self.frame.size.height);
  636. selectedSegmentOffset = (CGRectGetWidth(self.frame) / 2) - ([[self.segmentWidthsArray objectAtIndex:self.selectedSegmentIndex] floatValue] / 2);
  637. }
  638. CGRect rectToScrollTo = rectForSelectedIndex;
  639. rectToScrollTo.origin.x -= selectedSegmentOffset;
  640. rectToScrollTo.size.width += selectedSegmentOffset * 2;
  641. [self.scrollView scrollRectToVisible:rectToScrollTo animated:animated];
  642. }
  643. #pragma mark - Index Change
  644. - (void)setSelectedSegmentIndex:(NSInteger)index {
  645. [self setSelectedSegmentIndex:index animated:NO notify:NO];
  646. }
  647. - (void)setSelectedSegmentIndex:(NSUInteger)index animated:(BOOL)animated {
  648. [self setSelectedSegmentIndex:index animated:animated notify:NO];
  649. }
  650. - (void)setSelectedSegmentIndex:(NSUInteger)index animated:(BOOL)animated notify:(BOOL)notify {
  651. _selectedSegmentIndex = index;
  652. [self setNeedsDisplay];
  653. if (index == HMSegmentedControlNoSegment) {
  654. [self.selectionIndicatorArrowLayer removeFromSuperlayer];
  655. [self.selectionIndicatorStripLayer removeFromSuperlayer];
  656. [self.selectionIndicatorBoxLayer removeFromSuperlayer];
  657. } else {
  658. [self scrollToSelectedSegmentIndex:animated];
  659. if (animated) {
  660. // If the selected segment layer is not added to the super layer, that means no
  661. // index is currently selected, so add the layer then move it to the new
  662. // segment index without animating.
  663. if(self.selectionStyle == HMSegmentedControlSelectionStyleArrow) {
  664. if ([self.selectionIndicatorArrowLayer superlayer] == nil) {
  665. [self.scrollView.layer addSublayer:self.selectionIndicatorArrowLayer];
  666. [self setSelectedSegmentIndex:index animated:NO notify:YES];
  667. return;
  668. }
  669. }else {
  670. if ([self.selectionIndicatorStripLayer superlayer] == nil) {
  671. [self.scrollView.layer addSublayer:self.selectionIndicatorStripLayer];
  672. if (self.selectionStyle == HMSegmentedControlSelectionStyleBox && [self.selectionIndicatorBoxLayer superlayer] == nil)
  673. [self.scrollView.layer insertSublayer:self.selectionIndicatorBoxLayer atIndex:0];
  674. [self setSelectedSegmentIndex:index animated:NO notify:YES];
  675. return;
  676. }
  677. }
  678. if (notify)
  679. [self notifyForSegmentChangeToIndex:index];
  680. // Restore CALayer animations
  681. self.selectionIndicatorArrowLayer.actions = nil;
  682. self.selectionIndicatorStripLayer.actions = nil;
  683. self.selectionIndicatorBoxLayer.actions = nil;
  684. // Animate to new position
  685. [CATransaction begin];
  686. [CATransaction setAnimationDuration:0.15f];
  687. [CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
  688. [self setArrowFrame];
  689. self.selectionIndicatorBoxLayer.frame = [self frameForSelectionIndicator];
  690. self.selectionIndicatorStripLayer.frame = [self frameForSelectionIndicator];
  691. //rk- 7.28
  692. self.selectionIndicatorStripLayer.masksToBounds = YES;
  693. self.selectionIndicatorStripLayer.cornerRadius = self.selectionIndicatorStripLayer.frame.size.height/2;
  694. self.selectionIndicatorBoxLayer.frame = [self frameForFillerSelectionIndicator];
  695. [CATransaction commit];
  696. } else {
  697. // Disable CALayer animations
  698. NSMutableDictionary *newActions = [[NSMutableDictionary alloc] initWithObjectsAndKeys:[NSNull null], @"position", [NSNull null], @"bounds", nil];
  699. self.selectionIndicatorArrowLayer.actions = newActions;
  700. [self setArrowFrame];
  701. self.selectionIndicatorStripLayer.actions = newActions;
  702. self.selectionIndicatorStripLayer.frame = [self frameForSelectionIndicator];
  703. //rk- 7.28
  704. self.selectionIndicatorStripLayer.masksToBounds = YES;
  705. self.selectionIndicatorStripLayer.cornerRadius = self.selectionIndicatorStripLayer.frame.size.height/2;
  706. self.selectionIndicatorBoxLayer.actions = newActions;
  707. self.selectionIndicatorBoxLayer.frame = [self frameForFillerSelectionIndicator];
  708. if (notify)
  709. [self notifyForSegmentChangeToIndex:index];
  710. }
  711. }
  712. }
  713. - (void)notifyForSegmentChangeToIndex:(NSInteger)index {
  714. if (self.superview)
  715. [self sendActionsForControlEvents:UIControlEventValueChanged];
  716. if (self.indexChangeBlock)
  717. self.indexChangeBlock(index);
  718. }
  719. #pragma mark - Styling Support
  720. - (NSDictionary *)resultingTitleTextAttributes {
  721. NSDictionary *defaults = @{
  722. NSFontAttributeName : [UIFont systemFontOfSize:19.0f],
  723. NSForegroundColorAttributeName : [UIColor blackColor],
  724. };
  725. NSMutableDictionary *resultingAttrs = [NSMutableDictionary dictionaryWithDictionary:defaults];
  726. if (self.titleTextAttributes) {
  727. [resultingAttrs addEntriesFromDictionary:self.titleTextAttributes];
  728. }
  729. return [resultingAttrs copy];
  730. }
  731. - (NSDictionary *)resultingSelectedTitleTextAttributes {
  732. NSMutableDictionary *resultingAttrs = [NSMutableDictionary dictionaryWithDictionary:[self resultingTitleTextAttributes]];
  733. if (self.selectedTitleTextAttributes) {
  734. [resultingAttrs addEntriesFromDictionary:self.selectedTitleTextAttributes];
  735. }
  736. return [resultingAttrs copy];
  737. }
  738. @end