Guest User

redSegmentedControl.m

a guest
Aug 17th, 2013
156
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.57 KB | None | 0 0
  1. #import "redSegmentedControl.h"
  2. #import <QuartzCore/QuartzCore.h>
  3.  
  4. @interface redSegmentedControl ()
  5.  
  6. @property (nonatomic, strong) CALayer *selectionIndicatorStripLayer;
  7. @property (nonatomic, strong) CALayer *selectionIndicatorBoxLayer;
  8. @property (nonatomic, readwrite) CGFloat segmentWidth;
  9.  
  10. @end
  11.  
  12. @implementation redSegmentedControl
  13.  
  14.  
  15. - (id)initWithCoder:(NSCoder *)aDecoder
  16. {
  17. self = [super initWithCoder:aDecoder];
  18. if (self) {
  19. [self setDefaults];
  20. }
  21. return self;
  22. }
  23.  
  24. - (id)initWithFrame:(CGRect)frame
  25. {
  26. self = [super initWithFrame:frame];
  27.  
  28. if (self)
  29. {
  30. [self setDefaults];
  31. }
  32.  
  33. return self;
  34. }
  35.  
  36. - (id)initWithSectionTitles:(NSArray *)sectiontitles
  37. {
  38. self = [super initWithFrame:CGRectZero];
  39.  
  40. if (self) {
  41. self.sectionTitles = sectiontitles;
  42. [self setDefaults];
  43. self.type = redSegmentedControlTypeText;
  44. }
  45.  
  46. return self;
  47. }
  48.  
  49. - (id)initWithSectionImages:(NSArray*)sectionImages sectionSelectedImages:(NSArray*)sectionSelectedImages
  50. {
  51. self = [super initWithFrame:CGRectZero];
  52.  
  53. if (self) {
  54. self.sectionImages = sectionImages;
  55. self.sectionSelectedImages = sectionSelectedImages;
  56. [self setDefaults];
  57. self.type = redSegmentedControlTypeImages;
  58. }
  59.  
  60. return self;
  61. }
  62.  
  63. - (void)setDefaults {
  64. self.font = [UIFont fontWithName:@"STHeitiSC-Light" size:18.0f];
  65. self.textColor = [UIColor blackColor];
  66. self.selectedTextColor = [UIColor blackColor];
  67. self.backgroundColor = [UIColor whiteColor];
  68. self.opaque = NO;
  69. self.selectionIndicatorColor = [UIColor colorWithRed:52.0f/255.0f green:181.0f/255.0f blue:229.0f/255.0f alpha:1.0f];
  70.  
  71. self.selectedSegmentIndex = 0;
  72. self.segmentEdgeInset = UIEdgeInsetsMake(0, 5, 0, 5);
  73. self.height = 32.0f;
  74. self.selectionIndicatorHeight = 5.0f;
  75. self.selectionStyle = redSegmentedControlSelectionStyleTextWidthStrip;
  76. self.selectionLocation = redSegmentedControlSelectionLocationUp;
  77. self.type = redSegmentedControlTypeText;
  78.  
  79. self.selectionIndicatorStripLayer = [CALayer layer];
  80.  
  81. self.selectionIndicatorBoxLayer = [CALayer layer];
  82. self.selectionIndicatorBoxLayer.opacity = 0.2;
  83. self.selectionIndicatorBoxLayer.borderWidth = 1.0f;
  84. }
  85.  
  86. #pragma mark - Drawing
  87.  
  88. - (void)drawRect:(CGRect)rect {
  89. [self.backgroundColor setFill];
  90. UIRectFill([self bounds]);
  91.  
  92. self.selectionIndicatorStripLayer.backgroundColor = self.selectionIndicatorColor.CGColor;
  93. self.selectionIndicatorBoxLayer.backgroundColor = self.selectionIndicatorColor.CGColor;
  94. self.selectionIndicatorBoxLayer.borderColor = self.selectionIndicatorColor.CGColor;
  95.  
  96. // Remove all sublayers to avoid drawing images over existing ones
  97. self.layer.sublayers = nil;
  98.  
  99. if (self.type == redSegmentedControlTypeText) {
  100. [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx, BOOL *stop) {
  101. CGFloat stringHeight = roundf([titleString sizeWithFont:self.font].height);
  102. CGFloat y = roundf(((self.height - self.selectionIndicatorHeight) / 2) + (self.selectionIndicatorHeight - stringHeight / 2));
  103. CGRect rect = CGRectMake(self.segmentWidth * idx, y, self.segmentWidth, stringHeight);
  104.  
  105. CATextLayer *titleLayer = [CATextLayer layer];
  106. // Note: text inside the CATextLayer will appear blurry unless the rect values around rounded
  107. titleLayer.frame = rect;
  108. [titleLayer setFont:(__bridge CFTypeRef)(self.font.fontName)];
  109. [titleLayer setFontSize:self.font.pointSize];
  110. [titleLayer setAlignmentMode:kCAAlignmentCenter];
  111. [titleLayer setString:titleString];
  112.  
  113. if (self.selectedSegmentIndex == idx)
  114. [titleLayer setForegroundColor:self.selectedTextColor.CGColor];
  115. else
  116. [titleLayer setForegroundColor:self.textColor.CGColor];
  117.  
  118. [titleLayer setContentsScale:[[UIScreen mainScreen] scale]];
  119. [self.layer addSublayer:titleLayer];
  120. }];
  121. } else if (self.type == redSegmentedControlTypeImages) {
  122. [self.sectionImages enumerateObjectsUsingBlock:^(id iconImage, NSUInteger idx, BOOL *stop) {
  123. UIImage *icon = iconImage;
  124. CGFloat imageWidth = icon.size.width;
  125. CGFloat imageHeight = icon.size.height;
  126. CGFloat y = ((self.height - self.selectionIndicatorHeight) / 2) + (self.selectionIndicatorHeight - imageHeight / 2);
  127. CGFloat x = self.segmentWidth * idx + (self.segmentWidth - imageWidth)/2.0f;
  128. CGRect rect = CGRectMake(x, y, imageWidth, imageHeight);
  129.  
  130. CALayer *imageLayer = [CALayer layer];
  131. [imageLayer setFrame:rect];
  132.  
  133. if (self.selectedSegmentIndex == idx) {
  134. if (self.sectionSelectedImages) {
  135. UIImage *highlightIcon = [self.sectionSelectedImages objectAtIndex:idx];
  136. imageLayer.contents = (id)highlightIcon.CGImage;
  137. } else {
  138. imageLayer.contents = (id)icon.CGImage;
  139. }
  140. } else {
  141. imageLayer.contents = (id)icon.CGImage;
  142. }
  143.  
  144. [self.layer addSublayer:imageLayer];
  145. }];
  146. }
  147.  
  148. // Add the selection indicators
  149. if (self.selectedSegmentIndex != redSegmentedControlNoSegment && !self.selectionIndicatorStripLayer.superlayer) {
  150. self.selectionIndicatorStripLayer.frame = [self frameForSelectionIndicator];
  151. [self.layer addSublayer:self.selectionIndicatorStripLayer];
  152.  
  153. if (self.selectionStyle == redSegmentedControlSelectionStyleBox && !self.selectionIndicatorBoxLayer.superlayer) {
  154. self.selectionIndicatorBoxLayer.frame = [self frameForFillerSelectionIndicator];
  155. [self.layer insertSublayer:self.selectionIndicatorBoxLayer atIndex:0];
  156. }
  157. }
  158. }
  159.  
  160. - (CGRect)frameForSelectionIndicator {
  161. CGFloat indicatorYOffset = 0.0f;
  162.  
  163. if (self.selectionLocation == redSegmentedControlSelectionLocationDown)
  164. indicatorYOffset = self.bounds.size.height - self.selectionIndicatorHeight;
  165.  
  166. CGFloat sectionWidth = 0.0f;
  167.  
  168. if (self.type == redSegmentedControlTypeText) {
  169. CGFloat stringWidth = [[self.sectionTitles objectAtIndex:self.selectedSegmentIndex] sizeWithFont:self.font].width;
  170. sectionWidth = stringWidth;
  171. } else if (self.type == redSegmentedControlTypeImages) {
  172. UIImage *sectionImage = [self.sectionImages objectAtIndex:self.selectedSegmentIndex];
  173. CGFloat imageWidth = sectionImage.size.width;
  174. sectionWidth = imageWidth;
  175. }
  176.  
  177. if (self.selectionStyle == redSegmentedControlSelectionStyleTextWidthStrip && sectionWidth <= self.segmentWidth) {
  178. CGFloat widthToEndOfSelectedSegment = (self.segmentWidth * self.selectedSegmentIndex) + self.segmentWidth;
  179. CGFloat widthToStartOfSelectedIndex = (self.segmentWidth * self.selectedSegmentIndex);
  180.  
  181. CGFloat x = ((widthToEndOfSelectedSegment - widthToStartOfSelectedIndex) / 2) + (widthToStartOfSelectedIndex - sectionWidth / 2);
  182. return CGRectMake(x, indicatorYOffset, sectionWidth, self.selectionIndicatorHeight);
  183. } else {
  184. return CGRectMake(self.segmentWidth * self.selectedSegmentIndex, indicatorYOffset, self.segmentWidth, self.selectionIndicatorHeight);
  185. }
  186. }
  187.  
  188. - (CGRect)frameForFillerSelectionIndicator {
  189. return CGRectMake(self.segmentWidth * self.selectedSegmentIndex, 0, self.segmentWidth, self.height);
  190. }
  191.  
  192. - (void)updateSegmentsRects {
  193. // If there's no frame set, calculate the width of the control based on the number of segments and their size
  194. if (CGRectIsEmpty(self.frame)) {
  195. self.segmentWidth = 0;
  196.  
  197. if (self.type == redSegmentedControlTypeText) {
  198. for (NSString *titleString in self.sectionTitles) {
  199. CGFloat stringWidth = [titleString sizeWithFont:self.font].width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
  200. self.segmentWidth = MAX(stringWidth, self.segmentWidth);
  201. }
  202. self.bounds = CGRectMake(0, 0, self.segmentWidth * self.sectionTitles.count, self.height);
  203. } else if (self.type == redSegmentedControlTypeImages) {
  204. for (UIImage *sectionImage in self.sectionImages) {
  205. CGFloat imageWidth = sectionImage.size.width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
  206. self.segmentWidth = MAX(imageWidth, self.segmentWidth);
  207. }
  208. self.bounds = CGRectMake(0, 0, self.segmentWidth * self.sectionImages.count, self.height);
  209. }
  210. } else {
  211. if (self.type == redSegmentedControlTypeText)
  212. self.segmentWidth = self.frame.size.width / self.sectionTitles.count;
  213. else if (self.type == redSegmentedControlTypeImages)
  214. self.segmentWidth = self.frame.size.width / self.sectionImages.count;
  215.  
  216. self.segmentWidth = roundf(self.segmentWidth);
  217. self.height = self.frame.size.height;
  218. }
  219. }
  220.  
  221. - (void)willMoveToSuperview:(UIView *)newSuperview {
  222. // Control is being removed
  223. if (newSuperview == nil)
  224. return;
  225.  
  226. [self updateSegmentsRects];
  227. }
  228.  
  229. #pragma mark - Touch
  230.  
  231. - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  232. UITouch *touch = [touches anyObject];
  233. CGPoint touchLocation = [touch locationInView:self];
  234.  
  235. if (CGRectContainsPoint(self.bounds, touchLocation)) {
  236. NSInteger segment = touchLocation.x / self.segmentWidth;
  237.  
  238. if (segment != self.selectedSegmentIndex) {
  239. [self setSelectedSegmentIndex:segment animated:YES notify:YES];
  240. }
  241. }
  242. }
  243.  
  244. #pragma mark -
  245.  
  246. - (void)setSelectedSegmentIndex:(NSInteger)index {
  247. [self setSelectedSegmentIndex:index animated:NO notify:NO];
  248. }
  249.  
  250. - (void)setSelectedSegmentIndex:(NSUInteger)index animated:(BOOL)animated
  251. {
  252. [self setSelectedSegmentIndex:index animated:animated notify:NO];
  253. }
  254.  
  255. - (void)setSelectedSegmentIndex:(NSUInteger)index animated:(BOOL)animated notify:(BOOL)notify {
  256. _selectedSegmentIndex = index;
  257. [self setNeedsDisplay];
  258.  
  259. if (index == redSegmentedControlNoSegment) {
  260. [self.selectionIndicatorStripLayer removeFromSuperlayer];
  261. [self.selectionIndicatorBoxLayer removeFromSuperlayer];
  262. } else {
  263. if (animated) {
  264.  
  265. /*
  266. If the selected segment layer is not added to the super layer, that means no
  267. index is currently selected, so add the layer then move it to the new
  268. segment index without animating.
  269. */
  270. if ([self.selectionIndicatorStripLayer superlayer] == nil) {
  271. [self.layer addSublayer:self.selectionIndicatorStripLayer];
  272.  
  273. if (self.selectionStyle == redSegmentedControlSelectionStyleBox && [self.selectionIndicatorBoxLayer superlayer] == nil)
  274. [self.layer insertSublayer:self.selectionIndicatorBoxLayer atIndex:0];
  275.  
  276. [self setSelectedSegmentIndex:index animated:NO notify:YES];
  277. return;
  278. }
  279.  
  280. if (notify)
  281. [self notifyForSegmentChangeToIndex:index];
  282.  
  283. // Restore CALayer animations
  284. self.selectionIndicatorStripLayer.actions = nil;
  285. self.selectionIndicatorBoxLayer.actions = nil;
  286.  
  287. // Animate to new position
  288. [CATransaction begin];
  289. [CATransaction setAnimationDuration:0.15f];
  290. [CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
  291. self.selectionIndicatorStripLayer.frame = [self frameForSelectionIndicator];
  292. self.selectionIndicatorBoxLayer.frame = [self frameForFillerSelectionIndicator];
  293. [CATransaction commit];
  294. } else {
  295. // Disable CALayer animations
  296. NSMutableDictionary *newActions = [[NSMutableDictionary alloc] initWithObjectsAndKeys:[NSNull null], @"position", [NSNull null], @"bounds", nil];
  297. self.selectionIndicatorStripLayer.actions = newActions;
  298. self.selectionIndicatorStripLayer.frame = [self frameForSelectionIndicator];
  299.  
  300. self.selectionIndicatorBoxLayer.actions = newActions;
  301. self.selectionIndicatorBoxLayer.frame = [self frameForFillerSelectionIndicator];
  302.  
  303. if (notify)
  304. [self notifyForSegmentChangeToIndex:index];
  305. }
  306. }
  307. }
  308.  
  309. - (void)notifyForSegmentChangeToIndex:(NSInteger)index {
  310. if (self.superview)
  311. [self sendActionsForControlEvents:UIControlEventValueChanged];
  312.  
  313. if (self.indexChangeBlock)
  314. self.indexChangeBlock(index);
  315. }
  316.  
  317. - (void)setFrame:(CGRect)frame {
  318. [super setFrame:frame];
  319.  
  320. if (self.type == redSegmentedControlTypeText && self.sectionTitles)
  321. [self updateSegmentsRects];
  322. else if(self.type == redSegmentedControlTypeImages && self.sectionImages)
  323. [self updateSegmentsRects];
  324.  
  325. [self setNeedsDisplay];
  326. }
  327.  
  328. - (void)setBounds:(CGRect)bounds {
  329. [super setBounds:bounds];
  330.  
  331. if (self.type == redSegmentedControlTypeText && self.sectionTitles)
  332. [self updateSegmentsRects];
  333. else if(self.type == redSegmentedControlTypeImages && self.sectionImages)
  334. [self updateSegmentsRects];
  335.  
  336. [self setNeedsDisplay];
  337. }
  338.  
  339. @end
Advertisement
Add Comment
Please, Sign In to add comment