123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- #import <QuartzCore/QuartzCore.h>
- #import "UICountingLabel.h"
- #if !__has_feature(objc_arc)
- #error UICountingLabel is ARC only. Either turn on ARC for the project or use -fobjc-arc flag
- #endif
- #pragma mark - UILabelCounter
- #ifndef kUILabelCounterRate
- #define kUILabelCounterRate 3.0
- #endif
- @protocol UILabelCounter<NSObject>
- -(CGFloat)update:(CGFloat)t;
- @end
- @interface UILabelCounterLinear : NSObject<UILabelCounter>
- @end
- @interface UILabelCounterEaseIn : NSObject<UILabelCounter>
- @end
- @interface UILabelCounterEaseOut : NSObject<UILabelCounter>
- @end
- @interface UILabelCounterEaseInOut : NSObject<UILabelCounter>
- @end
- @interface UILabelCounterEaseInBounce : NSObject<UILabelCounter>
- @end
- @interface UILabelCounterEaseOutBounce : NSObject<UILabelCounter>
- @end
- @implementation UILabelCounterLinear
- -(CGFloat)update:(CGFloat)t
- {
- return t;
- }
- @end
- @implementation UILabelCounterEaseIn
- -(CGFloat)update:(CGFloat)t
- {
- return powf(t, kUILabelCounterRate);
- }
- @end
- @implementation UILabelCounterEaseOut
- -(CGFloat)update:(CGFloat)t{
- return 1.0-powf((1.0-t), kUILabelCounterRate);
- }
- @end
- @implementation UILabelCounterEaseInOut
- -(CGFloat) update: (CGFloat) t
- {
- t *= 2;
- if (t < 1)
- return 0.5f * powf (t, kUILabelCounterRate);
- else
- return 0.5f * (2.0f - powf(2.0 - t, kUILabelCounterRate));
- }
- @end
- @implementation UILabelCounterEaseInBounce
- -(CGFloat) update: (CGFloat) t {
-
- if (t < 4.0 / 11.0) {
- return 1.0 - (powf(11.0 / 4.0, 2) * powf(t, 2)) - t;
- }
-
- if (t < 8.0 / 11.0) {
- return 1.0 - (3.0 / 4.0 + powf(11.0 / 4.0, 2) * powf(t - 6.0 / 11.0, 2)) - t;
- }
-
- if (t < 10.0 / 11.0) {
- return 1.0 - (15.0 /16.0 + powf(11.0 / 4.0, 2) * powf(t - 9.0 / 11.0, 2)) - t;
- }
-
- return 1.0 - (63.0 / 64.0 + powf(11.0 / 4.0, 2) * powf(t - 21.0 / 22.0, 2)) - t;
-
- }
- @end
- @implementation UILabelCounterEaseOutBounce
- -(CGFloat) update: (CGFloat) t {
-
- if (t < 4.0 / 11.0) {
- return powf(11.0 / 4.0, 2) * powf(t, 2);
- }
-
- if (t < 8.0 / 11.0) {
- return 3.0 / 4.0 + powf(11.0 / 4.0, 2) * powf(t - 6.0 / 11.0, 2);
- }
-
- if (t < 10.0 / 11.0) {
- return 15.0 /16.0 + powf(11.0 / 4.0, 2) * powf(t - 9.0 / 11.0, 2);
- }
-
- return 63.0 / 64.0 + powf(11.0 / 4.0, 2) * powf(t - 21.0 / 22.0, 2);
-
- }
- @end
- #pragma mark - UICountingLabel
- @interface UICountingLabel ()
- @property CGFloat startingValue;
- @property CGFloat destinationValue;
- @property NSTimeInterval progress;
- @property NSTimeInterval lastUpdate;
- @property NSTimeInterval totalTime;
- @property CGFloat easingRate;
- @property (nonatomic, strong) CADisplayLink *timer;
- @property (nonatomic, strong) id<UILabelCounter> counter;
- @end
- @implementation UICountingLabel
- -(void)countFrom:(CGFloat)value to:(CGFloat)endValue {
-
- if (self.animationDuration == 0.0f) {
- self.animationDuration = 2.0f;
- }
-
- [self countFrom:value to:endValue withDuration:self.animationDuration];
- }
- -(void)countFrom:(CGFloat)startValue to:(CGFloat)endValue withDuration:(NSTimeInterval)duration {
-
- self.startingValue = startValue;
- self.destinationValue = endValue;
-
- // remove any (possible) old timers
- [self.timer invalidate];
- self.timer = nil;
-
- if(self.format == nil) {
- self.format = @"%f";
- }
- if (duration == 0.0) {
- // No animation
- [self setTextValue:endValue];
- [self runCompletionBlock];
- return;
- }
- self.easingRate = 3.0f;
- self.progress = 0;
- self.totalTime = duration;
- self.lastUpdate = [NSDate timeIntervalSinceReferenceDate];
- switch(self.method)
- {
- case UILabelCountingMethodLinear:
- self.counter = [[UILabelCounterLinear alloc] init];
- break;
- case UILabelCountingMethodEaseIn:
- self.counter = [[UILabelCounterEaseIn alloc] init];
- break;
- case UILabelCountingMethodEaseOut:
- self.counter = [[UILabelCounterEaseOut alloc] init];
- break;
- case UILabelCountingMethodEaseInOut:
- self.counter = [[UILabelCounterEaseInOut alloc] init];
- break;
- case UILabelCountingMethodEaseOutBounce:
- self.counter = [[UILabelCounterEaseOutBounce alloc] init];
- break;
- case UILabelCountingMethodEaseInBounce:
- self.counter = [[UILabelCounterEaseInBounce alloc] init];
- break;
- }
- CADisplayLink *timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateValue:)];
- timer.frameInterval = 2;
- [timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
- [timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:UITrackingRunLoopMode];
- self.timer = timer;
- }
- - (void)countFromCurrentValueTo:(CGFloat)endValue {
- [self countFrom:[self currentValue] to:endValue];
- }
- - (void)countFromCurrentValueTo:(CGFloat)endValue withDuration:(NSTimeInterval)duration {
- [self countFrom:[self currentValue] to:endValue withDuration:duration];
- }
- - (void)countFromZeroTo:(CGFloat)endValue {
- [self countFrom:0.0f to:endValue];
- }
- - (void)countFromZeroTo:(CGFloat)endValue withDuration:(NSTimeInterval)duration {
- [self countFrom:0.0f to:endValue withDuration:duration];
- }
- - (void)updateValue:(NSTimer *)timer {
-
- // update progress
- NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
- self.progress += now - self.lastUpdate;
- self.lastUpdate = now;
-
- if (self.progress >= self.totalTime) {
- [self.timer invalidate];
- self.timer = nil;
- self.progress = self.totalTime;
- }
-
- [self setTextValue:[self currentValue]];
-
- if (self.progress == self.totalTime) {
- [self runCompletionBlock];
- }
- }
- - (void)setTextValue:(CGFloat)value
- {
- if (self.attributedFormatBlock != nil) {
- self.attributedText = self.attributedFormatBlock(value);
- }
- else if(self.formatBlock != nil)
- {
- self.text = self.formatBlock(value);
- }
- else
- {
- // check if counting with ints - cast to int
- if([self.format rangeOfString:@"%(.*)d" options:NSRegularExpressionSearch].location != NSNotFound || [self.format rangeOfString:@"%(.*)i"].location != NSNotFound )
- {
- NSString * str = [NSString stringWithFormat:self.format,(int)value];
- self.attributedText = [self setWithText:str];
- }
- else
- {
- NSString * str = [NSString stringWithFormat:self.format,value];
- self.attributedText = [self setWithText:str];
- }
- }
- }
- - (NSAttributedString *)setWithText:(NSString *)str
- {
- NSMutableAttributedString * attr = [[NSMutableAttributedString alloc] initWithString:str];
- [attr addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"PingFang SC" size:17] range:NSMakeRange(0, str.length - 1)];
- [attr addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"PingFang SC" size:14] range:NSMakeRange(str.length - 1, 1)];
- [attr addAttribute:NSForegroundColorAttributeName value:UIColorHex(0x0a0a0a) range:NSMakeRange(0, str.length)];
- return attr;
- }
- - (void)setFormat:(NSString *)format {
- _format = format;
- // update label with new format
- [self setTextValue:self.currentValue];
- }
- - (void)runCompletionBlock {
-
- if (self.completionBlock) {
- self.completionBlock();
- self.completionBlock = nil;
- }
- }
- - (CGFloat)currentValue {
-
- if (self.progress >= self.totalTime) {
- return self.destinationValue;
- }
-
- CGFloat percent = self.progress / self.totalTime;
- CGFloat updateVal = [self.counter update:percent];
- return self.startingValue + (updateVal * (self.destinationValue - self.startingValue));
- }
- @end
|