RMDownloadIndicator.m 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. //
  2. // RMDownloadIndicator.m
  3. // BezierLoaders
  4. //
  5. // Created by Mahesh on 1/30/14.
  6. // Copyright (c) 2014 Mahesh. All rights reserved.
  7. //
  8. #import "RMDownloadIndicator.h"
  9. #import "RMDisplayLabel.h"
  10. @interface RMDownloadIndicator()
  11. // this contains list of paths to be animated through
  12. @property(nonatomic, strong)NSMutableArray *paths;
  13. // the shaper layers used for display
  14. @property(nonatomic, strong)CAShapeLayer *indicateShapeLayer;
  15. @property(nonatomic, strong)CAShapeLayer *coverLayer;
  16. // this is the layer used for animation
  17. @property(nonatomic, strong)CAShapeLayer *animatingLayer;
  18. // the type of indicator
  19. @property(nonatomic, assign)RMIndicatorType type;
  20. // this applies to the covering stroke (default: 2)
  21. @property(nonatomic, assign)CGFloat coverWidth;
  22. // the last updatedPath
  23. @property(nonatomic, strong)UIBezierPath *lastUpdatedPath;
  24. @property(nonatomic, assign)CGFloat lastSourceAngle;
  25. // this the animation duration (default: 0.5)
  26. @property(nonatomic, assign)CGFloat animationDuration;
  27. // this is display label that displays % downloaded
  28. @property(nonatomic, strong)RMDisplayLabel *displayLabel;
  29. @end
  30. @implementation RMDownloadIndicator
  31. - (id)initWithFrame:(CGRect)frame
  32. {
  33. self = [super initWithFrame:frame];
  34. if (self) {
  35. // Initialization code
  36. _type = kRMFilledIndicator;
  37. [self initAttributes];
  38. }
  39. return self;
  40. }
  41. - (id)initWithFrame:(CGRect)frame type:(RMIndicatorType)type
  42. {
  43. self = [super initWithFrame:frame];
  44. if (self) {
  45. // Initialization code
  46. _type = type;
  47. [self initAttributes];
  48. }
  49. return self;
  50. }
  51. /*
  52. // Only override drawRect: if you perform custom drawing.
  53. // An empty implementation adversely affects performance during animation.
  54. - (void)drawRect:(CGRect)rect
  55. {
  56. // Drawing code
  57. }
  58. */
  59. - (void)initAttributes
  60. {
  61. // 首先设置半径的属性
  62. if(_type == kRMClosedIndicator)
  63. {
  64. self.radiusPercent = 0.5;
  65. _coverLayer = [CAShapeLayer layer];
  66. _animatingLayer = _coverLayer;
  67. // 设置填充颜色
  68. _fillColor = [UIColor clearColor];
  69. _strokeColor = [UIColor whiteColor];
  70. _closedIndicatorBackgroundStrokeColor = [UIColor colorWithRed:27./255 green:153./255 blue:225./255 alpha:1.0f];
  71. _coverWidth = 5.0;
  72. //[self addDisplayLabel];
  73. }
  74. else
  75. {
  76. if(_type == kRMFilledIndicator)
  77. {
  78. // only indicateShapeLayer
  79. _indicateShapeLayer = [CAShapeLayer layer];
  80. _animatingLayer = _indicateShapeLayer;
  81. self.radiusPercent = 0.5;
  82. _coverWidth = 2.0;
  83. }
  84. else
  85. {
  86. // indicateShapeLayer and coverLayer
  87. _indicateShapeLayer = [CAShapeLayer layer];
  88. _coverLayer = [CAShapeLayer layer];
  89. _animatingLayer = _indicateShapeLayer;
  90. _coverWidth = 2.0;
  91. self.radiusPercent = 0.4;
  92. }
  93. // set the fill color
  94. _fillColor = [UIColor whiteColor];
  95. _strokeColor = [UIColor whiteColor];
  96. _closedIndicatorBackgroundStrokeColor = [UIColor clearColor];
  97. }
  98. _animatingLayer.frame = self.bounds;
  99. [self.layer addSublayer:_animatingLayer];
  100. // path array
  101. _paths = [NSMutableArray array];
  102. // animation duration
  103. _animationDuration = 0.5;
  104. }
  105. - (void)addDisplayLabel
  106. {
  107. self.displayLabel = [[RMDisplayLabel alloc] initWithFrame:CGRectMake((CGRectGetWidth(self.bounds)/2 - 30/2), (CGRectGetHeight(self.bounds)/2 - 30/2), 30, 30)];
  108. self.displayLabel.backgroundColor = [UIColor clearColor];
  109. self.displayLabel.font = [UIFont fontWithName:@"HelveticaNeue-Bold" size:11.5];
  110. self.displayLabel.text = @"0";
  111. self.displayLabel.textColor = [UIColor grayColor];
  112. self.displayLabel.textAlignment = NSTextAlignmentCenter;
  113. [self addSubview:self.displayLabel];
  114. }
  115. - (void)loadIndicator
  116. {
  117. // set the initial Path
  118. CGPoint center = CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2);
  119. UIBezierPath *initialPath = [UIBezierPath bezierPath]; //empty path
  120. if(_type == kRMClosedIndicator)
  121. {
  122. [initialPath addArcWithCenter:center radius:(MIN(CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds))) startAngle:degreeToRadian(-90) endAngle:degreeToRadian(-90) clockwise:YES]; //add the arc
  123. }
  124. else
  125. {
  126. if(_type == kRMMixedIndictor)
  127. {
  128. [self setNeedsDisplay];
  129. }
  130. CGFloat radius = (MIN(CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds))/2) * self.radiusPercent;
  131. [initialPath addArcWithCenter:center radius:radius startAngle:degreeToRadian(-90) endAngle:degreeToRadian(-90) clockwise:YES]; //add the arc
  132. }
  133. _animatingLayer.path = initialPath.CGPath;
  134. _animatingLayer.strokeColor = _strokeColor.CGColor;
  135. _animatingLayer.fillColor = _fillColor.CGColor;
  136. _animatingLayer.lineWidth = _coverWidth;
  137. self.lastSourceAngle = degreeToRadian(-90);
  138. }
  139. #pragma mark -
  140. #pragma mark Helper Methods
  141. - (NSArray *)keyframePathsWithDuration:(CGFloat)duration lastUpdatedAngle:(CGFloat)lastUpdatedAngle newAngle:(CGFloat)newAngle radius:(CGFloat)radius type:(RMIndicatorType)type
  142. {
  143. NSUInteger frameCount = ceil(duration * 60);
  144. NSMutableArray *array = [NSMutableArray arrayWithCapacity:frameCount + 1];
  145. for (int frame = 0; frame <= frameCount; frame++)
  146. {
  147. CGFloat startAngle = degreeToRadian(-90);
  148. CGFloat endAngle = lastUpdatedAngle + (((newAngle - lastUpdatedAngle) * frame) / frameCount);
  149. [array addObject:(id)([self pathWithStartAngle:startAngle endAngle:endAngle radius:radius type:type].CGPath)];
  150. }
  151. return [NSArray arrayWithArray:array];
  152. }
  153. - (UIBezierPath *)pathWithStartAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle radius:(CGFloat)radius type:(RMIndicatorType)type
  154. {
  155. BOOL clockwise = startAngle < endAngle;
  156. UIBezierPath *path = [UIBezierPath bezierPath];
  157. CGPoint center = CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2);
  158. if(type == kRMClosedIndicator)
  159. {
  160. [path addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:clockwise];
  161. }
  162. else
  163. {
  164. [path moveToPoint:center];
  165. [path addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:clockwise];
  166. [path closePath];
  167. }
  168. return path;
  169. }
  170. - (void)drawRect:(CGRect)rect
  171. {
  172. if(_type == kRMMixedIndictor)
  173. {
  174. CGFloat radius = MIN(CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds))/2 - self.coverWidth;
  175. CGPoint center = CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2);
  176. UIBezierPath *coverPath = [UIBezierPath bezierPath]; //empty path
  177. [coverPath setLineWidth:_coverWidth];
  178. [coverPath addArcWithCenter:center radius:radius startAngle:0 endAngle:2*M_PI clockwise:YES]; //add the arc
  179. [_strokeColor set];
  180. [coverPath stroke];
  181. }
  182. else if (_type == kRMClosedIndicator)
  183. {
  184. CGFloat radius = (MIN(CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds))/2) - self.coverWidth;
  185. CGPoint center = CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2);
  186. UIBezierPath *coverPath = [UIBezierPath bezierPath]; //empty path
  187. [coverPath setLineWidth:_coverWidth];
  188. [coverPath addArcWithCenter:center radius:radius startAngle:0 endAngle:2*M_PI clockwise:YES]; //add the arc
  189. [_closedIndicatorBackgroundStrokeColor set];
  190. [coverPath setLineWidth:self.coverWidth];
  191. UIColor *fillColor = [UIColor clearColor];
  192. [fillColor setFill];
  193. [coverPath fill];
  194. [coverPath stroke];
  195. }
  196. }
  197. #pragma mark - update indicator
  198. - (void)updateWithTotalBytes:(CGFloat)bytes downloadedBytes:(CGFloat)downloadedBytes
  199. {
  200. _lastUpdatedPath = [UIBezierPath bezierPathWithCGPath:_animatingLayer.path];
  201. [_paths removeAllObjects];
  202. CGFloat destinationAngle = [self destinationAngleForRatio:(downloadedBytes/bytes)];
  203. CGFloat radius = (MIN(CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds)) * _radiusPercent) - self.coverWidth;
  204. [_paths addObjectsFromArray:[self keyframePathsWithDuration:self.animationDuration lastUpdatedAngle:self.lastSourceAngle newAngle:destinationAngle radius:radius type:_type]];
  205. _animatingLayer.path = (__bridge CGPathRef)((id)_paths[(_paths.count -1)]);
  206. self.lastSourceAngle = destinationAngle;
  207. CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"path"];
  208. [pathAnimation setValues:_paths];
  209. [pathAnimation setDuration:self.animationDuration];
  210. [pathAnimation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
  211. [pathAnimation setRemovedOnCompletion:YES];
  212. [_animatingLayer addAnimation:pathAnimation forKey:@"path"];
  213. //[self.displayLabel updateValue:downloadedBytes/bytes];
  214. }
  215. - (CGFloat)destinationAngleForRatio:(CGFloat)ratio
  216. {
  217. return (degreeToRadian((360*ratio) - 90));
  218. }
  219. float degreeToRadian(float degree)
  220. {
  221. return ((degree * M_PI)/180.0f);
  222. }
  223. #pragma mark -
  224. #pragma mark Setter Methods
  225. - (void)setFillColor:(UIColor *)fillColor
  226. {
  227. if(_type == kRMClosedIndicator)
  228. _fillColor = [UIColor clearColor];
  229. else
  230. _fillColor = fillColor;
  231. }
  232. - (void)setRadiusPercent:(CGFloat)radiusPercent
  233. {
  234. if(_type == kRMClosedIndicator)
  235. {
  236. _radiusPercent = 0.5;
  237. return;
  238. }
  239. if(radiusPercent > 0.5 || radiusPercent < 0)
  240. return;
  241. else
  242. _radiusPercent = radiusPercent;
  243. }
  244. - (void)setIndicatorAnimationDuration:(CGFloat)duration
  245. {
  246. self.animationDuration = duration;
  247. }
  248. @end