#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
|
|
@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
|
|
#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 (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];
|
|
if(self.format == nil)
|
self.format = @"%f";
|
|
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;
|
}
|
|
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 )
|
{
|
self.text = [NSString stringWithFormat:self.format,(int)value];
|
}
|
else
|
{
|
self.text = [NSString stringWithFormat:self.format,value];
|
}
|
}
|
}
|
|
- (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
|