PageRenderTime 78ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/FoxKit/Controls/FXSegmented/FXSegmented.m

https://gitlab.com/foxsofter/FoxKit
Objective C | 998 lines | 826 code | 165 blank | 7 comment | 195 complexity | f4a72ad9644b2cc5c68788a651e1c442 MD5 | raw file
  1. //
  2. // FXSegmented.m
  3. // DDPanGu
  4. //
  5. // Created by foxsofter on 15/2/13.
  6. // Copyright (c) 2015ๅนด foxsofter. All rights reserved.
  7. //
  8. #import "FXSegmented.h"
  9. @implementation FXScrollView
  10. - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  11. if (!self.dragging) {
  12. [self.nextResponder touchesBegan:touches withEvent:event];
  13. } else {
  14. [super touchesBegan:touches withEvent:event];
  15. }
  16. }
  17. - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
  18. if (!self.dragging) {
  19. [self.nextResponder touchesMoved:touches withEvent:event];
  20. } else {
  21. [super touchesMoved:touches withEvent:event];
  22. }
  23. }
  24. - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
  25. if (!self.dragging) {
  26. [self.nextResponder touchesEnded:touches withEvent:event];
  27. } else {
  28. [super touchesEnded:touches withEvent:event];
  29. }
  30. }
  31. @end
  32. @interface FXSegmented ()
  33. @property(nonatomic, strong) CALayer *selectionIndicatorStripLayer;
  34. @property(nonatomic, strong) CALayer *selectionIndicatorBoxLayer;
  35. @property(nonatomic, strong) CALayer *selectionIndicatorArrowLayer;
  36. @property(nonatomic, readwrite) CGFloat segmentWidth;
  37. @property(nonatomic, readwrite) NSArray *segmentWidthsArray;
  38. @property(nonatomic, strong) FXScrollView *scrollView;
  39. @property (nonatomic, assign, readwrite) CGFloat actualWidth;
  40. @end
  41. @implementation FXSegmented
  42. - (instancetype)initWithCoder:(NSCoder *)aDecoder {
  43. self = [super initWithCoder:aDecoder];
  44. if (self) {
  45. [self initialize];
  46. }
  47. return self;
  48. }
  49. - (instancetype)initWithFrame:(CGRect)frame {
  50. self = [super initWithFrame:frame];
  51. if (self) {
  52. [self initialize];
  53. }
  54. return self;
  55. }
  56. - (instancetype)initWithSectionTitles:(NSArray *)sectionTitles {
  57. self = [self initWithFrame:CGRectZero];
  58. if (self) {
  59. [self initialize];
  60. self.sectionTitles = sectionTitles;
  61. self.type = FXSegmentedTypeText;
  62. }
  63. return self;
  64. }
  65. - (instancetype)initWithSectionImages:(NSArray *)sectionImages
  66. sectionSelectedImages:(NSArray *)sectionSelectedImages {
  67. self = [super initWithFrame:CGRectZero];
  68. if (self) {
  69. [self initialize];
  70. self.sectionImages = sectionImages;
  71. self.sectionSelectedImages = sectionSelectedImages;
  72. self.type = FXSegmentedTypeImages;
  73. }
  74. return self;
  75. }
  76. - (instancetype)initWithSectionImages:(NSArray *)sectionImages
  77. sectionSelectedImages:(NSArray *)sectionSelectedImages
  78. titlesForSections:(NSArray *)sectiontitles {
  79. self = [super initWithFrame:CGRectZero];
  80. if (self) {
  81. [self initialize];
  82. if (sectionImages.count != sectiontitles.count) {
  83. [NSException
  84. raise:NSRangeException
  85. format:@"***%s: Images bounds (%ld) Dont match Title bounds (%ld)",
  86. sel_getName(_cmd), (unsigned long)sectionImages.count,
  87. (unsigned long)sectiontitles.count];
  88. }
  89. self.sectionImages = sectionImages;
  90. self.sectionSelectedImages = sectionSelectedImages;
  91. self.sectionTitles = sectiontitles;
  92. self.type = FXSegmentedTypeTextImages;
  93. }
  94. return self;
  95. }
  96. - (void)awakeFromNib {
  97. [super awakeFromNib];
  98. self.segmentWidth = 0.0f;
  99. [self initialize];
  100. }
  101. - (void)initialize {
  102. self.scrollView = [[FXScrollView alloc] init];
  103. self.scrollView.scrollsToTop = NO;
  104. self.scrollView.showsVerticalScrollIndicator = NO;
  105. self.scrollView.showsHorizontalScrollIndicator = NO;
  106. [self addSubview:self.scrollView];
  107. _backgroundColor = [UIColor whiteColor];
  108. self.opaque = NO;
  109. _selectionIndicatorColor = [UIColor colorWithRed:52.0f / 255.0f
  110. green:181.0f / 255.0f
  111. blue:229.0f / 255.0f
  112. alpha:1.0f];
  113. self.selectedIndex = 0;
  114. self.segmentEdgeInset = UIEdgeInsetsMake(0, 5, 0, 5);
  115. self.selectionIndicatorHeight = 5.0f;
  116. self.selectionIndicatorEdgeInsets = UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f);
  117. self.selectionStyle = FXSegmentedSelectionStyleTextWidthStripe;
  118. self.selectionIndicatorLocation = FXSegmentedSelectionIndicatorLocationTop;
  119. self.segmentWidthStyle = FXSegmentedWidthStyleFixed;
  120. self.userDraggable = YES;
  121. self.touchEnabled = YES;
  122. self.verticalDividerEnabled = NO;
  123. self.type = FXSegmentedTypeText;
  124. self.verticalDividerWidth = 1.0f;
  125. _verticalDividerColor = [UIColor blackColor];
  126. self.borderColor = [UIColor blackColor];
  127. self.borderWidth = 1.0f;
  128. self.shouldAnimateUserSelection = YES;
  129. self.selectionIndicatorArrowLayer = [CALayer layer];
  130. self.selectionIndicatorStripLayer = [CALayer layer];
  131. self.selectionIndicatorBoxLayer = [CALayer layer];
  132. self.selectionIndicatorBoxLayer.opacity = self.selectionIndicatorBoxOpacity;
  133. self.selectionIndicatorBoxLayer.borderWidth = 1.0f;
  134. self.selectionIndicatorBoxOpacity = 0.2;
  135. self.contentMode = UIViewContentModeRedraw;
  136. }
  137. - (void)layoutSubviews {
  138. [super layoutSubviews];
  139. [self updateSegmentsRects];
  140. }
  141. - (void)setFrame:(CGRect)frame {
  142. [super setFrame:frame];
  143. [self updateSegmentsRects];
  144. }
  145. -(void)setCenter:(CGPoint)center {
  146. [super setCenter:center];
  147. [self updateSegmentsRects];
  148. }
  149. - (void)setSectionTitles:(NSArray *)sectionTitles {
  150. _sectionTitles = sectionTitles;
  151. [self setNeedsLayout];
  152. }
  153. - (void)setSectionImages:(NSArray *)sectionImages {
  154. _sectionImages = sectionImages;
  155. [self setNeedsLayout];
  156. }
  157. - (void)setSelectionIndicatorLocation:
  158. (FXSegmentedSelectionIndicatorLocation)selectionIndicatorLocation {
  159. _selectionIndicatorLocation = selectionIndicatorLocation;
  160. if (selectionIndicatorLocation == FXSegmentedSelectionIndicatorLocationNone) {
  161. self.selectionIndicatorHeight = 0.0f;
  162. }
  163. }
  164. - (void)setSelectionIndicatorBoxOpacity:(CGFloat)selectionIndicatorBoxOpacity {
  165. _selectionIndicatorBoxOpacity = selectionIndicatorBoxOpacity;
  166. self.selectionIndicatorBoxLayer.opacity = _selectionIndicatorBoxOpacity;
  167. }
  168. - (void)setSegmentWidthStyle:(FXSegmentedWidthStyle)widthStyle {
  169. if (self.type == FXSegmentedTypeImages) {
  170. _widthStyle = FXSegmentedWidthStyleFixed;
  171. } else {
  172. _widthStyle = widthStyle;
  173. }
  174. }
  175. - (void)setBorderType:(FXSegmentedBorderType)borderType {
  176. _borderType = borderType;
  177. [self setNeedsDisplay];
  178. }
  179. #pragma mark - Drawing
  180. - (CGSize)measureTitleAtIndex:(NSUInteger)index {
  181. id title = self.sectionTitles[index];
  182. CGSize size = CGSizeZero;
  183. BOOL selected = (index == self.selectedIndex) ? YES : NO;
  184. if ([title isKindOfClass:[NSString class]] && !self.titleFormatter) {
  185. NSDictionary *titleAttrs = selected
  186. ? [self resultingSelectedTitleTextAttributes]
  187. : [self resultingTitleTextAttributes];
  188. size = [(NSString *)title sizeWithAttributes:titleAttrs];
  189. } else if ([title isKindOfClass:[NSString class]] && self.titleFormatter) {
  190. size = [self.titleFormatter(self, title, index, selected) size];
  191. } else if ([title isKindOfClass:[NSAttributedString class]]) {
  192. size = [(NSAttributedString *)title size];
  193. } else {
  194. NSAssert(title == nil, @"Unexpected type of segment title: %@",
  195. [title class]);
  196. size = CGSizeZero;
  197. }
  198. return CGRectIntegral((CGRect){CGPointZero, size}).size;
  199. }
  200. - (NSAttributedString *)attributedTitleAtIndex:(NSUInteger)index {
  201. NSString *title = self.sectionTitles[index];
  202. BOOL selected = (index == self.selectedIndex) ? YES : NO;
  203. if (!self.titleFormatter) {
  204. NSDictionary *titleAttrs = selected
  205. ? [self resultingSelectedTitleTextAttributes]
  206. : [self resultingTitleTextAttributes];
  207. return [[NSAttributedString alloc] initWithString:(NSString *)title
  208. attributes:titleAttrs];
  209. } else {
  210. return self.titleFormatter(self, title, index, selected);
  211. }
  212. }
  213. - (void)drawRect:(CGRect)rect {
  214. [self.backgroundColor setFill];
  215. UIRectFill([self bounds]);
  216. self.selectionIndicatorArrowLayer.backgroundColor =
  217. self.selectionIndicatorColor.CGColor;
  218. self.selectionIndicatorStripLayer.backgroundColor =
  219. self.selectionIndicatorColor.CGColor;
  220. self.selectionIndicatorBoxLayer.backgroundColor =
  221. self.selectionIndicatorColor.CGColor;
  222. self.selectionIndicatorBoxLayer.borderColor =
  223. self.selectionIndicatorColor.CGColor;
  224. self.scrollView.layer.sublayers = nil;
  225. CGRect oldRect = rect;
  226. if (self.type == FXSegmentedTypeText) {
  227. [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString,
  228. NSUInteger idx,
  229. BOOL *stop) {
  230. CGFloat stringWidth = 0;
  231. CGFloat stringHeight = 0;
  232. CGSize size = [self measureTitleAtIndex:idx];
  233. stringWidth = size.width;
  234. stringHeight = size.height;
  235. CGRect rectDiv, fullRect;
  236. BOOL locationUp = (self.selectionIndicatorLocation ==
  237. FXSegmentedSelectionIndicatorLocationTop);
  238. BOOL selectionStyleNotBox =
  239. (self.selectionStyle != FXSegmentedSelectionStyleBox);
  240. CGFloat y =
  241. roundf((CGRectGetHeight(self.frame) -
  242. selectionStyleNotBox * self.selectionIndicatorHeight) /
  243. 2 -
  244. stringHeight / 2 + self.selectionIndicatorHeight * locationUp);
  245. CGRect rect;
  246. if (self.widthStyle == FXSegmentedWidthStyleFixed) {
  247. rect = CGRectMake(
  248. (self.segmentWidth * idx) + (self.segmentWidth - stringWidth) / 2,
  249. y, stringWidth, stringHeight);
  250. rectDiv = CGRectMake(
  251. (self.segmentWidth * idx) - (self.verticalDividerWidth / 2),
  252. self.selectionIndicatorHeight * 2, self.verticalDividerWidth,
  253. self.frame.size.height - (self.selectionIndicatorHeight * 4));
  254. fullRect = CGRectMake(self.segmentWidth * idx, 0, self.segmentWidth,
  255. oldRect.size.height);
  256. } else if (self.widthStyle == FXSegmentedWidthStyleDynamic) {
  257. CGFloat xOffset = 0;
  258. NSInteger i = 0;
  259. for (NSNumber *width in self.segmentWidthsArray) {
  260. if (idx == i) break;
  261. xOffset = xOffset + [width floatValue];
  262. i++;
  263. }
  264. CGFloat widthForIndex = [(self.segmentWidthsArray)[idx] floatValue];
  265. rect = CGRectMake(xOffset, y, widthForIndex, stringHeight);
  266. fullRect = CGRectMake(self.segmentWidth * idx, 0, widthForIndex,
  267. oldRect.size.height);
  268. rectDiv = CGRectMake(
  269. xOffset - (self.verticalDividerWidth / 2),
  270. self.selectionIndicatorHeight * 2, self.verticalDividerWidth,
  271. self.frame.size.height - (self.selectionIndicatorHeight * 4));
  272. }
  273. rect = CGRectMake(ceilf(rect.origin.x), ceilf(rect.origin.y),
  274. ceilf(rect.size.width), ceilf(rect.size.height));
  275. CATextLayer *titleLayer = [CATextLayer layer];
  276. titleLayer.frame = rect;
  277. titleLayer.alignmentMode = kCAAlignmentCenter;
  278. titleLayer.truncationMode = kCATruncationEnd;
  279. titleLayer.string = [self attributedTitleAtIndex:idx];
  280. titleLayer.contentsScale = [[UIScreen mainScreen] scale];
  281. [self.scrollView.layer addSublayer:titleLayer];
  282. if (self.isVerticalDividerEnabled && idx > 0) {
  283. CALayer *verticalDividerLayer = [CALayer layer];
  284. verticalDividerLayer.frame = rectDiv;
  285. verticalDividerLayer.backgroundColor =
  286. self.verticalDividerColor.CGColor;
  287. [self.scrollView.layer addSublayer:verticalDividerLayer];
  288. }
  289. [self addBackgroundAndBorderLayerWithRect:fullRect];
  290. }];
  291. } else if (self.type == FXSegmentedTypeImages) {
  292. [self.sectionImages
  293. enumerateObjectsUsingBlock:^(id iconImage, NSUInteger idx, BOOL *stop) {
  294. UIImage *icon = iconImage;
  295. CGFloat imageWidth = icon.size.width;
  296. CGFloat imageHeight = icon.size.height;
  297. CGFloat y = roundf(CGRectGetHeight(self.frame) -
  298. self.selectionIndicatorHeight) /
  299. 2 -
  300. imageHeight / 2 +
  301. ((self.selectionIndicatorLocation ==
  302. FXSegmentedSelectionIndicatorLocationTop)
  303. ? self.selectionIndicatorHeight
  304. : 0);
  305. CGFloat x =
  306. self.segmentWidth * idx + (self.segmentWidth - imageWidth) / 2.0f;
  307. CGRect rect = CGRectMake(x, y, imageWidth, imageHeight);
  308. CALayer *imageLayer = [CALayer layer];
  309. imageLayer.frame = rect;
  310. if (self.selectedIndex == idx) {
  311. if (self.sectionSelectedImages) {
  312. UIImage *highlightIcon = (self.sectionSelectedImages)[idx];
  313. imageLayer.contents = (id)highlightIcon.CGImage;
  314. } else {
  315. imageLayer.contents = (id)icon.CGImage;
  316. }
  317. } else {
  318. imageLayer.contents = (id)icon.CGImage;
  319. }
  320. [self.scrollView.layer addSublayer:imageLayer];
  321. if (self.isVerticalDividerEnabled && idx > 0) {
  322. CALayer *verticalDividerLayer = [CALayer layer];
  323. verticalDividerLayer.frame = CGRectMake(
  324. (self.segmentWidth * idx) - (self.verticalDividerWidth / 2),
  325. self.selectionIndicatorHeight * 2, self.verticalDividerWidth,
  326. self.frame.size.height - (self.selectionIndicatorHeight * 4));
  327. verticalDividerLayer.backgroundColor =
  328. self.verticalDividerColor.CGColor;
  329. [self.scrollView.layer addSublayer:verticalDividerLayer];
  330. }
  331. [self addBackgroundAndBorderLayerWithRect:rect];
  332. }];
  333. } else if (self.type == FXSegmentedTypeTextImages) {
  334. [self.sectionImages
  335. enumerateObjectsUsingBlock:^(id iconImage, NSUInteger idx, BOOL *stop) {
  336. UIImage *icon = iconImage;
  337. CGFloat imageWidth = icon.size.width;
  338. CGFloat imageHeight = icon.size.height;
  339. CGFloat stringHeight = [self measureTitleAtIndex:idx].height;
  340. CGFloat yOffset = roundf(
  341. ((CGRectGetHeight(self.frame) - self.selectionIndicatorHeight) /
  342. 2) -
  343. (stringHeight / 2));
  344. CGFloat imageXOffset = self.segmentEdgeInset.left;
  345. CGFloat textXOffset = self.segmentEdgeInset.left;
  346. CGFloat textWidth = 0;
  347. if (self.widthStyle == FXSegmentedWidthStyleFixed) {
  348. imageXOffset = (self.segmentWidth * idx) +
  349. (self.segmentWidth / 2.0f) - (imageWidth / 2.0f);
  350. textXOffset = self.segmentWidth * idx;
  351. textWidth = self.segmentWidth;
  352. } else if (self.widthStyle == FXSegmentedWidthStyleDynamic) {
  353. CGFloat xOffset = 0;
  354. NSInteger i = 0;
  355. for (NSNumber *width in self.segmentWidthsArray) {
  356. if (idx == i) {
  357. break;
  358. }
  359. xOffset = xOffset + [width floatValue];
  360. i++;
  361. }
  362. imageXOffset = xOffset +
  363. ([self.segmentWidthsArray[idx] floatValue] / 2.0f) -
  364. (imageWidth / 2.0f);
  365. textXOffset = xOffset;
  366. textWidth = [self.segmentWidthsArray[idx] floatValue];
  367. }
  368. CGFloat imageYOffset = roundf(
  369. (CGRectGetHeight(self.frame) - self.selectionIndicatorHeight) /
  370. 2.0f);
  371. CGRect imageRect =
  372. CGRectMake(imageXOffset, imageYOffset, imageWidth, imageHeight);
  373. CGRect textRect =
  374. CGRectMake(textXOffset, yOffset, textWidth, stringHeight);
  375. textRect = CGRectMake(
  376. ceilf(textRect.origin.x), ceilf(textRect.origin.y),
  377. ceilf(textRect.size.width), ceilf(textRect.size.height));
  378. CATextLayer *titleLayer = [CATextLayer layer];
  379. titleLayer.frame = textRect;
  380. titleLayer.alignmentMode = kCAAlignmentCenter;
  381. titleLayer.string = [self attributedTitleAtIndex:idx];
  382. titleLayer.truncationMode = kCATruncationEnd;
  383. CALayer *imageLayer = [CALayer layer];
  384. imageLayer.frame = imageRect;
  385. if (self.selectedIndex == idx) {
  386. if (self.sectionSelectedImages) {
  387. UIImage *highlightIcon = (self.sectionSelectedImages)[idx];
  388. imageLayer.contents = (id)highlightIcon.CGImage;
  389. } else {
  390. imageLayer.contents = (id)icon.CGImage;
  391. }
  392. } else {
  393. imageLayer.contents = (id)icon.CGImage;
  394. }
  395. [self.scrollView.layer addSublayer:imageLayer];
  396. titleLayer.contentsScale = [[UIScreen mainScreen] scale];
  397. [self.scrollView.layer addSublayer:titleLayer];
  398. [self addBackgroundAndBorderLayerWithRect:imageRect];
  399. }];
  400. }
  401. if (self.selectedIndex != FXSegmentedNoSegment) {
  402. if (self.selectionStyle == FXSegmentedSelectionStyleArrow) {
  403. if (!self.selectionIndicatorArrowLayer.superlayer) {
  404. [self setArrowFrame];
  405. [self.scrollView.layer addSublayer:self.selectionIndicatorArrowLayer];
  406. }
  407. } else {
  408. if (!self.selectionIndicatorStripLayer.superlayer) {
  409. self.selectionIndicatorStripLayer.frame =
  410. [self frameForSelectionIndicator];
  411. [self.scrollView.layer addSublayer:self.selectionIndicatorStripLayer];
  412. if (self.selectionStyle == FXSegmentedSelectionStyleBox &&
  413. !self.selectionIndicatorBoxLayer.superlayer) {
  414. self.selectionIndicatorBoxLayer.frame =
  415. [self frameForFillerSelectionIndicator];
  416. [self.scrollView.layer insertSublayer:self.selectionIndicatorBoxLayer
  417. atIndex:0];
  418. }
  419. }
  420. }
  421. }
  422. }
  423. - (void)addBackgroundAndBorderLayerWithRect:(CGRect)fullRect {
  424. CALayer *backgroundLayer = [CALayer layer];
  425. backgroundLayer.frame = fullRect;
  426. [self.scrollView.layer insertSublayer:backgroundLayer atIndex:0];
  427. if (self.borderType & FXSegmentedBorderTypeTop) {
  428. CALayer *borderLayer = [CALayer layer];
  429. borderLayer.frame = CGRectMake(0, 0, fullRect.size.width, self.borderWidth);
  430. borderLayer.backgroundColor = self.borderColor.CGColor;
  431. [backgroundLayer addSublayer:borderLayer];
  432. }
  433. if (self.borderType & FXSegmentedBorderTypeLeft) {
  434. CALayer *borderLayer = [CALayer layer];
  435. borderLayer.frame =
  436. CGRectMake(0, 0, self.borderWidth, fullRect.size.height);
  437. borderLayer.backgroundColor = self.borderColor.CGColor;
  438. [backgroundLayer addSublayer:borderLayer];
  439. }
  440. if (self.borderType & FXSegmentedBorderTypeBottom) {
  441. CALayer *borderLayer = [CALayer layer];
  442. borderLayer.frame = CGRectMake(0, fullRect.size.height - self.borderWidth,
  443. fullRect.size.width, self.borderWidth);
  444. borderLayer.backgroundColor = self.borderColor.CGColor;
  445. [backgroundLayer addSublayer:borderLayer];
  446. }
  447. if (self.borderType & FXSegmentedBorderTypeRight) {
  448. CALayer *borderLayer = [CALayer layer];
  449. borderLayer.frame = CGRectMake(fullRect.size.width - self.borderWidth, 0,
  450. self.borderWidth, fullRect.size.height);
  451. borderLayer.backgroundColor = self.borderColor.CGColor;
  452. [backgroundLayer addSublayer:borderLayer];
  453. }
  454. }
  455. - (void)setArrowFrame {
  456. self.selectionIndicatorArrowLayer.frame = [self frameForSelectionIndicator];
  457. self.selectionIndicatorArrowLayer.mask = nil;
  458. UIBezierPath *arrowPath = [UIBezierPath bezierPath];
  459. CGPoint p1 = CGPointZero;
  460. CGPoint p2 = CGPointZero;
  461. CGPoint p3 = CGPointZero;
  462. if (self.selectionIndicatorLocation ==
  463. FXSegmentedSelectionIndicatorLocationBottom) {
  464. p1 =
  465. CGPointMake(self.selectionIndicatorArrowLayer.bounds.size.width / 2, 0);
  466. p2 = CGPointMake(0, self.selectionIndicatorArrowLayer.bounds.size.height);
  467. p3 = CGPointMake(self.selectionIndicatorArrowLayer.bounds.size.width,
  468. self.selectionIndicatorArrowLayer.bounds.size.height);
  469. }
  470. if (self.selectionIndicatorLocation ==
  471. FXSegmentedSelectionIndicatorLocationTop) {
  472. p1 = CGPointMake(self.selectionIndicatorArrowLayer.bounds.size.width / 2,
  473. self.selectionIndicatorArrowLayer.bounds.size.height);
  474. p2 = CGPointMake(self.selectionIndicatorArrowLayer.bounds.size.width, 0);
  475. p3 = CGPointMake(0, 0);
  476. }
  477. [arrowPath moveToPoint:p1];
  478. [arrowPath addLineToPoint:p2];
  479. [arrowPath addLineToPoint:p3];
  480. [arrowPath closePath];
  481. CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
  482. maskLayer.frame = self.selectionIndicatorArrowLayer.bounds;
  483. maskLayer.path = arrowPath.CGPath;
  484. self.selectionIndicatorArrowLayer.mask = maskLayer;
  485. }
  486. - (CGRect)frameForSelectionIndicator {
  487. CGFloat indicatorYOffset = 0.0f;
  488. if (self.selectionIndicatorLocation ==
  489. FXSegmentedSelectionIndicatorLocationBottom) {
  490. indicatorYOffset = self.bounds.size.height - self.selectionIndicatorHeight +
  491. self.selectionIndicatorEdgeInsets.bottom;
  492. }
  493. if (self.selectionIndicatorLocation ==
  494. FXSegmentedSelectionIndicatorLocationTop) {
  495. indicatorYOffset = self.selectionIndicatorEdgeInsets.top;
  496. }
  497. CGFloat sectionWidth = 0.0f;
  498. if (self.type == FXSegmentedTypeText) {
  499. CGFloat stringWidth = [self measureTitleAtIndex:self.selectedIndex].width;
  500. sectionWidth = stringWidth;
  501. } else if (self.type == FXSegmentedTypeImages) {
  502. UIImage *sectionImage = (self.sectionImages)[self.selectedIndex];
  503. CGFloat imageWidth = sectionImage.size.width;
  504. sectionWidth = imageWidth;
  505. } else if (self.type == FXSegmentedTypeTextImages) {
  506. CGFloat stringWidth = [self measureTitleAtIndex:self.selectedIndex].width;
  507. UIImage *sectionImage = (self.sectionImages)[self.selectedIndex];
  508. CGFloat imageWidth = sectionImage.size.width;
  509. sectionWidth = MAX(stringWidth, imageWidth);
  510. }
  511. if (self.selectionStyle == FXSegmentedSelectionStyleArrow) {
  512. CGFloat widthToEndOfSelectedSegment =
  513. (self.segmentWidth * self.selectedIndex) + self.segmentWidth;
  514. CGFloat widthToStartOfSelectedIndex =
  515. (self.segmentWidth * self.selectedIndex);
  516. CGFloat x =
  517. widthToStartOfSelectedIndex +
  518. ((widthToEndOfSelectedSegment - widthToStartOfSelectedIndex) / 2) -
  519. (self.selectionIndicatorHeight / 2);
  520. return CGRectMake(x - (self.selectionIndicatorHeight / 2), indicatorYOffset,
  521. self.selectionIndicatorHeight * 2,
  522. self.selectionIndicatorHeight);
  523. } else {
  524. if (self.selectionStyle == FXSegmentedSelectionStyleTextWidthStripe &&
  525. sectionWidth <= self.segmentWidth &&
  526. self.widthStyle != FXSegmentedWidthStyleDynamic) {
  527. CGFloat widthToEndOfSelectedSegment =
  528. (self.segmentWidth * self.selectedIndex) + self.segmentWidth;
  529. CGFloat widthToStartOfSelectedIndex =
  530. (self.segmentWidth * self.selectedIndex);
  531. CGFloat x =
  532. ((widthToEndOfSelectedSegment - widthToStartOfSelectedIndex) / 2) +
  533. (widthToStartOfSelectedIndex - sectionWidth / 2);
  534. return CGRectMake(x + self.selectionIndicatorEdgeInsets.left,
  535. indicatorYOffset,
  536. sectionWidth - self.selectionIndicatorEdgeInsets.right,
  537. self.selectionIndicatorHeight);
  538. } else {
  539. if (self.widthStyle == FXSegmentedWidthStyleDynamic) {
  540. CGFloat selectedSegmentOffset = 0.0f;
  541. NSInteger i = 0;
  542. for (NSNumber *width in self.segmentWidthsArray) {
  543. if (self.selectedIndex == i) break;
  544. selectedSegmentOffset = selectedSegmentOffset + [width floatValue];
  545. i++;
  546. }
  547. return CGRectMake(
  548. selectedSegmentOffset + self.selectionIndicatorEdgeInsets.left,
  549. indicatorYOffset,
  550. [(self.segmentWidthsArray)[self.selectedIndex] floatValue] -
  551. self.selectionIndicatorEdgeInsets.right,
  552. self.selectionIndicatorHeight +
  553. self.selectionIndicatorEdgeInsets.bottom);
  554. }
  555. return CGRectMake(
  556. (self.segmentWidth + self.selectionIndicatorEdgeInsets.left) *
  557. self.selectedIndex,
  558. indicatorYOffset,
  559. self.segmentWidth - self.selectionIndicatorEdgeInsets.right,
  560. self.selectionIndicatorHeight);
  561. }
  562. }
  563. }
  564. - (CGRect)frameForFillerSelectionIndicator {
  565. if (self.widthStyle == FXSegmentedWidthStyleDynamic) {
  566. CGFloat selectedSegmentOffset = 0.0f;
  567. NSInteger i = 0;
  568. for (NSNumber *width in self.segmentWidthsArray) {
  569. if (self.selectedIndex == i) {
  570. break;
  571. }
  572. selectedSegmentOffset = selectedSegmentOffset + [width floatValue];
  573. i++;
  574. }
  575. return CGRectMake(
  576. selectedSegmentOffset, 0,
  577. [(self.segmentWidthsArray)[self.selectedIndex] floatValue],
  578. CGRectGetHeight(self.frame));
  579. }
  580. return CGRectMake(self.segmentWidth * self.selectedIndex, 0,
  581. self.segmentWidth, CGRectGetHeight(self.frame));
  582. }
  583. - (void)updateSegmentsRects {
  584. self.scrollView.contentInset = UIEdgeInsetsZero;
  585. self.scrollView.frame =
  586. CGRectMake(0, 0, CGRectGetWidth(self.frame), CGRectGetHeight(self.frame));
  587. if ([self sectionCount] > 0) {
  588. self.segmentWidth = self.frame.size.width / [self sectionCount];
  589. }
  590. if (self.type == FXSegmentedTypeText &&
  591. self.widthStyle == FXSegmentedWidthStyleFixed) {
  592. [self.sectionTitles
  593. enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx,
  594. BOOL *stop) {
  595. CGFloat stringWidth = [self measureTitleAtIndex:idx].width +
  596. self.segmentEdgeInset.left +
  597. self.segmentEdgeInset.right;
  598. self.segmentWidth = MAX(stringWidth, self.segmentWidth);
  599. }];
  600. } else if (self.type == FXSegmentedTypeText &&
  601. self.widthStyle == FXSegmentedWidthStyleDynamic) {
  602. NSMutableArray *mutableSegmentWidths = [NSMutableArray array];
  603. [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString,
  604. NSUInteger idx,
  605. BOOL *stop) {
  606. CGFloat stringWidth = [self measureTitleAtIndex:idx].width +
  607. self.segmentEdgeInset.left +
  608. self.segmentEdgeInset.right;
  609. [mutableSegmentWidths addObject:[NSNumber numberWithFloat:stringWidth]];
  610. }];
  611. self.segmentWidthsArray = [mutableSegmentWidths copy];
  612. } else if (self.type == FXSegmentedTypeImages) {
  613. for (UIImage *sectionImage in self.sectionImages) {
  614. CGFloat imageWidth = sectionImage.size.width +
  615. self.segmentEdgeInset.left +
  616. self.segmentEdgeInset.right;
  617. self.segmentWidth = MAX(imageWidth, self.segmentWidth);
  618. }
  619. } else if (self.type == FXSegmentedTypeTextImages &&
  620. self.widthStyle == FXSegmentedWidthStyleFixed) {
  621. [self.sectionTitles
  622. enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx,
  623. BOOL *stop) {
  624. CGFloat stringWidth = [self measureTitleAtIndex:idx].width +
  625. self.segmentEdgeInset.left +
  626. self.segmentEdgeInset.right;
  627. self.segmentWidth = MAX(stringWidth, self.segmentWidth);
  628. }];
  629. } else if (self.type == FXSegmentedTypeTextImages &&
  630. FXSegmentedWidthStyleDynamic) {
  631. NSMutableArray *mutableSegmentWidths = [NSMutableArray array];
  632. int i = 0;
  633. [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString,
  634. NSUInteger idx,
  635. BOOL *stop) {
  636. CGFloat stringWidth =
  637. [self measureTitleAtIndex:idx].width + self.segmentEdgeInset.right;
  638. UIImage *sectionImage = (self.sectionImages)[i];
  639. CGFloat imageWidth = sectionImage.size.width + self.segmentEdgeInset.left;
  640. CGFloat combinedWidth = MAX(imageWidth, stringWidth);
  641. [mutableSegmentWidths addObject:[NSNumber numberWithFloat:combinedWidth]];
  642. }];
  643. self.segmentWidthsArray = [mutableSegmentWidths copy];
  644. }
  645. self.scrollView.scrollEnabled = self.isUserDraggable;
  646. self.scrollView.contentSize =
  647. CGSizeMake([self totalSegmentedControlWidth], self.frame.size.height);
  648. }
  649. - (NSUInteger)sectionCount {
  650. if (self.type == FXSegmentedTypeText) {
  651. return self.sectionTitles.count;
  652. } else if (self.type == FXSegmentedTypeImages ||
  653. self.type == FXSegmentedTypeTextImages) {
  654. return self.sectionImages.count;
  655. }
  656. return 0;
  657. }
  658. - (void)willMoveToSuperview:(UIView *)newSuperview {
  659. if (newSuperview == nil) return;
  660. if (self.sectionTitles || self.sectionImages) {
  661. [self updateSegmentsRects];
  662. }
  663. }
  664. #pragma mark - Touch
  665. - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
  666. UITouch *touch = [touches anyObject];
  667. CGPoint touchLocation = [touch locationInView:self];
  668. if (CGRectContainsPoint(self.bounds, touchLocation)) {
  669. NSInteger segment = 0;
  670. if (self.widthStyle == FXSegmentedWidthStyleFixed) {
  671. segment = (touchLocation.x + self.scrollView.contentOffset.x) /
  672. self.segmentWidth;
  673. } else if (self.widthStyle == FXSegmentedWidthStyleDynamic) {
  674. CGFloat widthLeft = (touchLocation.x + self.scrollView.contentOffset.x);
  675. for (NSNumber *width in self.segmentWidthsArray) {
  676. widthLeft = widthLeft - [width floatValue];
  677. if (widthLeft <= 0) break;
  678. segment++;
  679. }
  680. }
  681. NSUInteger sectionsCount = 0;
  682. if (self.type == FXSegmentedTypeImages) {
  683. sectionsCount = [self.sectionImages count];
  684. } else if (self.type == FXSegmentedTypeTextImages ||
  685. self.type == FXSegmentedTypeText) {
  686. sectionsCount = [self.sectionTitles count];
  687. }
  688. if (segment != self.selectedIndex && segment < sectionsCount) {
  689. if (self.isTouchEnabled)
  690. [self setSelectedIndex:segment
  691. animated:self.shouldAnimateUserSelection
  692. notify:YES];
  693. }
  694. }
  695. }
  696. #pragma mark - Scrolling
  697. - (CGFloat)totalSegmentedControlWidth {
  698. if (self.type == FXSegmentedTypeText &&
  699. self.widthStyle == FXSegmentedWidthStyleFixed) {
  700. _actualWidth = self.sectionTitles.count * self.segmentWidth;
  701. } else if (self.widthStyle == FXSegmentedWidthStyleDynamic) {
  702. _actualWidth = [[self.segmentWidthsArray valueForKeyPath:@"@sum.self"] floatValue];
  703. } else {
  704. _actualWidth = (CGFloat)(self.sectionImages.count * self.segmentWidth);
  705. }
  706. return _actualWidth;
  707. }
  708. - (void)scrollToSelectedSegmentIndex:(BOOL)animated {
  709. CGRect rectForSelectedIndex;
  710. CGFloat selectedSegmentOffset = 0;
  711. if (self.widthStyle == FXSegmentedWidthStyleFixed) {
  712. rectForSelectedIndex =
  713. CGRectMake(self.segmentWidth * self.selectedIndex, 0, self.segmentWidth,
  714. self.frame.size.height);
  715. selectedSegmentOffset =
  716. (CGRectGetWidth(self.frame) / 2) - (self.segmentWidth / 2);
  717. } else if (self.widthStyle == FXSegmentedWidthStyleDynamic) {
  718. NSInteger i = 0;
  719. CGFloat offsetter = 0;
  720. for (NSNumber *width in self.segmentWidthsArray) {
  721. if (self.selectedIndex == i) break;
  722. offsetter = offsetter + [width floatValue];
  723. i++;
  724. }
  725. rectForSelectedIndex =
  726. CGRectMake(offsetter, 0,
  727. [(self.segmentWidthsArray)[self.selectedIndex] floatValue],
  728. self.frame.size.height);
  729. selectedSegmentOffset =
  730. (CGRectGetWidth(self.frame) / 2) -
  731. ([(self.segmentWidthsArray)[self.selectedIndex] floatValue] / 2);
  732. }
  733. CGRect rectToScrollTo = rectForSelectedIndex;
  734. rectToScrollTo.origin.x -= selectedSegmentOffset;
  735. rectToScrollTo.size.width += selectedSegmentOffset * 2;
  736. [self.scrollView scrollRectToVisible:rectToScrollTo animated:animated];
  737. }
  738. #pragma mark - Index Change
  739. - (void)setSelectedIndex:(NSInteger)index {
  740. [self setSelectedIndex:index animated:NO notify:NO];
  741. }
  742. - (void)setSelectedIndex:(NSUInteger)index animated:(BOOL)animated {
  743. [self setSelectedIndex:index animated:animated notify:NO];
  744. }
  745. - (void)setSelectedIndex:(NSUInteger)index
  746. animated:(BOOL)animated
  747. notify:(BOOL)notify {
  748. _selectedIndex = index;
  749. [self setNeedsDisplay];
  750. if (index == FXSegmentedNoSegment) {
  751. [self.selectionIndicatorArrowLayer removeFromSuperlayer];
  752. [self.selectionIndicatorStripLayer removeFromSuperlayer];
  753. [self.selectionIndicatorBoxLayer removeFromSuperlayer];
  754. } else {
  755. [self scrollToSelectedSegmentIndex:animated];
  756. if (animated) {
  757. if (self.selectionStyle == FXSegmentedSelectionStyleArrow) {
  758. if ([self.selectionIndicatorArrowLayer superlayer] == nil) {
  759. [self.scrollView.layer addSublayer:self.selectionIndicatorArrowLayer];
  760. [self setSelectedIndex:index animated:NO notify:YES];
  761. return;
  762. }
  763. } else {
  764. if ([self.selectionIndicatorStripLayer superlayer] == nil) {
  765. [self.scrollView.layer addSublayer:self.selectionIndicatorStripLayer];
  766. if (self.selectionStyle == FXSegmentedSelectionStyleBox &&
  767. [self.selectionIndicatorBoxLayer superlayer] == nil)
  768. [self.scrollView.layer
  769. insertSublayer:self.selectionIndicatorBoxLayer
  770. atIndex:0];
  771. [self setSelectedIndex:index animated:NO notify:YES];
  772. return;
  773. }
  774. }
  775. if (notify) [self notifyForSegmentChangeToIndex:index];
  776. self.selectionIndicatorArrowLayer.actions = nil;
  777. self.selectionIndicatorStripLayer.actions = nil;
  778. self.selectionIndicatorBoxLayer.actions = nil;
  779. [CATransaction begin];
  780. [CATransaction setAnimationDuration:0.15f];
  781. [CATransaction setAnimationTimingFunction:
  782. [CAMediaTimingFunction
  783. functionWithName:kCAMediaTimingFunctionLinear]];
  784. [self setArrowFrame];
  785. self.selectionIndicatorBoxLayer.frame = [self frameForSelectionIndicator];
  786. self.selectionIndicatorStripLayer.frame =
  787. [self frameForSelectionIndicator];
  788. self.selectionIndicatorBoxLayer.frame =
  789. [self frameForFillerSelectionIndicator];
  790. [CATransaction commit];
  791. } else {
  792. NSMutableDictionary *newActions = [[NSMutableDictionary alloc]
  793. initWithObjectsAndKeys:[NSNull null], @"position", [NSNull null],
  794. @"bounds", nil];
  795. self.selectionIndicatorArrowLayer.actions = newActions;
  796. [self setArrowFrame];
  797. self.selectionIndicatorStripLayer.actions = newActions;
  798. self.selectionIndicatorStripLayer.frame =
  799. [self frameForSelectionIndicator];
  800. self.selectionIndicatorBoxLayer.actions = newActions;
  801. self.selectionIndicatorBoxLayer.frame =
  802. [self frameForFillerSelectionIndicator];
  803. if (notify) [self notifyForSegmentChangeToIndex:index];
  804. }
  805. }
  806. }
  807. - (void)notifyForSegmentChangeToIndex:(NSInteger)index {
  808. if (self.superview)
  809. [self sendActionsForControlEvents:UIControlEventValueChanged];
  810. if (self.selectionChanged) self.selectionChanged(index);
  811. }
  812. #pragma mark - Styling Support
  813. - (NSDictionary *)resultingTitleTextAttributes {
  814. NSDictionary *defaults = @{
  815. NSFontAttributeName : [UIFont fontWithName:@"STHeitiSC-Light" size:18.0f],
  816. NSForegroundColorAttributeName : [UIColor blackColor],
  817. };
  818. NSMutableDictionary *resultingAttrs =
  819. [NSMutableDictionary dictionaryWithDictionary:defaults];
  820. if (self.titleTextAttributes) {
  821. [resultingAttrs addEntriesFromDictionary:self.titleTextAttributes];
  822. }
  823. return [resultingAttrs copy];
  824. }
  825. - (NSDictionary *)resultingSelectedTitleTextAttributes {
  826. NSMutableDictionary *resultingAttrs = [NSMutableDictionary
  827. dictionaryWithDictionary:[self resultingTitleTextAttributes]];
  828. if (self.selectedTitleTextAttributes) {
  829. [resultingAttrs addEntriesFromDictionary:self.selectedTitleTextAttributes];
  830. }
  831. return [resultingAttrs copy];
  832. }
  833. @end