悟空记账

UICountingLabel.m 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. #import "UICountingLabel.h"
  2. #if !__has_feature(objc_arc)
  3. #error UICountingLabel is ARC only. Either turn on ARC for the project or use -fobjc-arc flag
  4. #endif
  5. #pragma mark - UILabelCounter
  6. // This whole class & subclasses are private to UICountingLabel, which is why they are declared here in the .m file
  7. @interface UILabelCounter : NSObject
  8. -(float)update:(float)t;
  9. @property float rate;
  10. @end
  11. @interface UILabelCounterLinear : UILabelCounter
  12. @end
  13. @interface UILabelCounterEaseIn : UILabelCounter
  14. @end
  15. @interface UILabelCounterEaseOut : UILabelCounter
  16. @end
  17. @interface UILabelCounterEaseInOut : UILabelCounter
  18. @end
  19. @implementation UILabelCounter
  20. -(float)update:(float)t{
  21. return 0;
  22. }
  23. @end
  24. @implementation UILabelCounterLinear
  25. -(float)update:(float)t
  26. {
  27. return t;
  28. }
  29. @end
  30. @implementation UILabelCounterEaseIn
  31. -(float)update:(float)t
  32. {
  33. return powf(t, self.rate);
  34. }
  35. @end
  36. @implementation UILabelCounterEaseOut
  37. -(float)update:(float)t{
  38. return 1.0-powf((1.0-t), self.rate);
  39. }
  40. @end
  41. @implementation UILabelCounterEaseInOut
  42. -(float) update: (float) t
  43. {
  44. int sign =1;
  45. int r = (int) self.rate;
  46. if (r % 2 == 0)
  47. sign = -1;
  48. t *= 2;
  49. if (t < 1)
  50. return 0.5f * powf (t, self.rate);
  51. else
  52. return sign*0.5f * (powf (t-2, self.rate) + sign*2);
  53. }
  54. @end
  55. #pragma mark - UICountingLabel
  56. @interface UICountingLabel ()
  57. @property float startingValue;
  58. @property float destinationValue;
  59. @property NSTimeInterval progress;
  60. @property NSTimeInterval lastUpdate;
  61. @property NSTimeInterval totalTime;
  62. @property float easingRate;
  63. @property (nonatomic, weak) NSTimer *timer;
  64. @property (nonatomic, strong) UILabelCounter *counter;
  65. @end
  66. @implementation UICountingLabel
  67. -(void)countFrom:(float)value to:(float)endValue {
  68. if (self.animationDuration == 0.0f) {
  69. self.animationDuration = 2.0f;
  70. }
  71. [self countFrom:value to:endValue withDuration:self.animationDuration];
  72. }
  73. -(void)countFrom:(float)startValue to:(float)endValue withDuration:(NSTimeInterval)duration {
  74. self.startingValue = startValue;
  75. self.destinationValue = endValue;
  76. // remove any (possible) old timers
  77. [self.timer invalidate];
  78. self.timer = nil;
  79. if (duration == 0.0) {
  80. // No animation
  81. [self setTextValue:endValue];
  82. [self runCompletionBlock];
  83. return;
  84. }
  85. self.easingRate = 3.0f;
  86. self.progress = 0;
  87. self.totalTime = duration;
  88. self.lastUpdate = [NSDate timeIntervalSinceReferenceDate];
  89. if(self.format == nil)
  90. self.format = @"%f";
  91. switch(self.method)
  92. {
  93. case UILabelCountingMethodLinear:
  94. self.counter = [[UILabelCounterLinear alloc] init];
  95. break;
  96. case UILabelCountingMethodEaseIn:
  97. self.counter = [[UILabelCounterEaseIn alloc] init];
  98. break;
  99. case UILabelCountingMethodEaseOut:
  100. self.counter = [[UILabelCounterEaseOut alloc] init];
  101. break;
  102. case UILabelCountingMethodEaseInOut:
  103. self.counter = [[UILabelCounterEaseInOut alloc] init];
  104. break;
  105. }
  106. self.counter.rate = 3.0f;
  107. NSTimer *timer = [NSTimer timerWithTimeInterval:(1.0f/30.0f) target:self selector:@selector(updateValue:) userInfo:nil repeats:YES];
  108. [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
  109. [[NSRunLoop mainRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
  110. self.timer = timer;
  111. }
  112. - (void)countFromCurrentValueTo:(float)endValue {
  113. [self countFrom:[self currentValue] to:endValue];
  114. }
  115. - (void)countFromCurrentValueTo:(float)endValue withDuration:(NSTimeInterval)duration {
  116. [self countFrom:[self currentValue] to:endValue withDuration:duration];
  117. }
  118. - (void)countFromZeroTo:(float)endValue {
  119. [self countFrom:0.0f to:endValue];
  120. }
  121. - (void)countFromZeroTo:(float)endValue withDuration:(NSTimeInterval)duration {
  122. [self countFrom:0.0f to:endValue withDuration:duration];
  123. }
  124. - (void)updateValue:(NSTimer *)timer {
  125. // update progress
  126. NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
  127. self.progress += now - self.lastUpdate;
  128. self.lastUpdate = now;
  129. if (self.progress >= self.totalTime) {
  130. [self.timer invalidate];
  131. self.timer = nil;
  132. self.progress = self.totalTime;
  133. }
  134. [self setTextValue:[self currentValue]];
  135. if (self.progress == self.totalTime) {
  136. [self runCompletionBlock];
  137. }
  138. }
  139. - (void)setTextValue:(float)value
  140. {
  141. if (self.attributedFormatBlock != nil) {
  142. self.attributedText = self.attributedFormatBlock(value);
  143. }
  144. else if(self.formatBlock != nil)
  145. {
  146. self.text = self.formatBlock(value);
  147. }
  148. else
  149. {
  150. // check if counting with ints - cast to int
  151. if([self.format rangeOfString:@"%(.*)d" options:NSRegularExpressionSearch].location != NSNotFound || [self.format rangeOfString:@"%(.*)i"].location != NSNotFound )
  152. {
  153. self.text = [NSString stringWithFormat:self.format,(int)value];
  154. }
  155. else
  156. {
  157. self.text = [NSString stringWithFormat:self.format,value];
  158. }
  159. }
  160. }
  161. - (void)setFormat:(NSString *)format {
  162. _format = format;
  163. // update label with new format
  164. [self setTextValue:self.currentValue];
  165. }
  166. - (void)runCompletionBlock {
  167. if (self.completionBlock) {
  168. self.completionBlock();
  169. self.completionBlock = nil;
  170. }
  171. }
  172. - (CGFloat)currentValue {
  173. if (self.progress >= self.totalTime) {
  174. return self.destinationValue;
  175. }
  176. CGFloat percent = self.progress / self.totalTime;
  177. CGFloat updateVal = [self.counter update:percent];
  178. return self.startingValue + (updateVal * (self.destinationValue - self.startingValue));
  179. }
  180. @end