1、数字动态变化
-
具体实现代码见 GitHub 源码 QExtension
-
QCountingLabel.h
/// 文本数字变化方式枚举 typedef NS_ENUM(NSUInteger, QCountingMethod) { QCountingMethodEaseInOut, // 开始结束慢,中间快 QCountingMethodEaseIn, // 开始慢,结束快 QCountingMethodEaseOut, // 开始快,结束慢 QCountingMethodLinear // 匀速 }; @interface QCountingLabel : UILabel /// 文本数字样式,默认为 @"%f" @property (nonatomic, strong) NSString *format; /// 文本数字分隔符样式,例如 @"###,##0.00" @property (nonatomic, strong) NSString *positiveFormat; /// 文本数字变化方式,默认为 EaseInOut @property (nonatomic, assign) QCountingMethod method; /// 文本数字变化时间,默认为 2.0 @property (nonatomic, assign) NSTimeInterval animationDuration; /// 文本数字样式 Block @property (nonatomic, copy) NSString *(^formatBlock)(CGFloat); /// 富文本数字样式 Block @property (nonatomic, copy) NSAttributedString *(^attributedFormatBlock)(CGFloat); /// 文本数字变化完成回调 Block @property (nonatomic, copy) void (^completionBlock)(); /** * 文本数字在指定时间内从起始值变化到结束值 * * @param frame 控件的 frame * @param format 文本数字样式,默认为 @"%f" * @param positiveFormat 文本数字分隔符样式 * @param method 文本数字变化方式,默认为 EaseInOut * @param startValue 起始值 * @param endValue 结束值 * @param duration 变化时间 * @param completion 完成回调 * * @return QCountingLabel 对象 */ + (instancetype)q_countingLabelWithFrame:(CGRect)frame format:(NSString *)format positiveFormat:(nullable NSString *)positiveFormat method:(QCountingMethod)method fromValue:(CGFloat)startValue toValue:(CGFloat)endValue withDuration:(NSTimeInterval)duration completion:(void (^)())completion; /** * 文本数字从起始值变化到结束值 * * <p> 默认变化时间 2.0 秒 <p> * * @param startValue 起始值 * @param endValue 结束值 * * @return nil */ - (void)q_countFromValue:(CGFloat)startValue toValue:(CGFloat)endValue; /** * 文本数字在指定时间内从起始值变化到结束值 * * @param startValue 起始值 * @param endValue 结束值 * @param duration 变化时间 * * @return nil */ - (void)q_countFromValue:(CGFloat)startValue toValue:(CGFloat)endValue withDuration:(NSTimeInterval)duration; /** * 文本数字从当前值变化到结束值 * * <p> 默认变化时间 2.0 秒 <p> * * @param endValue 结束值 * * @return nil */ - (void)q_countFromCurrentValueToValue:(CGFloat)endValue; /** * 文本数字在指定时间内从当前值变化到结束值 * * @param endValue 结束值 * @param duration 变化时间 * * @return nil */ - (void)q_countFromCurrentValueToValue:(CGFloat)endValue withDuration:(NSTimeInterval)duration; /** * 文本数字从 0 变化到结束值 * * <p> 默认变化时间 2.0 秒 <p> * * @param endValue 结束值 * * @return nil */ - (void)q_countFromZeroToValue:(CGFloat)endValue; /** * 文本数字在指定时间内从 0 变化到结束值 * * @param endValue 结束值 * @param duration 变化时间 * * @return nil */ - (void)q_countFromZeroToValue:(CGFloat)endValue withDuration:(NSTimeInterval)duration; @end
-
QCountingLabel.m
#ifndef kQLabelCounterRate #define kQLabelCounterRate 3.0 #endif NS_ASSUME_NONNULL_BEGIN @interface QCountingLabel () @property (nonatomic, assign) CGFloat startingValue; @property (nonatomic, assign) CGFloat destinationValue; @property (nonatomic, assign) NSTimeInterval progress; @property (nonatomic, assign) NSTimeInterval lastUpdate; @property (nonatomic, assign) NSTimeInterval totalTime; @property (nonatomic, assign) CGFloat easingRate; @property (nonatomic, strong, nullable) CADisplayLink *timer; @end @implementation QCountingLabel #pragma mark - 文本数字变化方法 /// 创建 QCountingLabel 对象 + (instancetype)q_countingLabelWithFrame:(CGRect)frame format:(NSString *)format positiveFormat:(nullable NSString *)positiveFormat method:(QCountingMethod)method fromValue:(CGFloat)startValue toValue:(CGFloat)endValue withDuration:(NSTimeInterval)duration completion:(void (^)())completion { QCountingLabel *label = [[self alloc] initWithFrame:frame]; label.format = format; label.positiveFormat = positiveFormat; label.method = method; label.completionBlock = completion; [label q_countFromValue:startValue toValue:endValue withDuration:duration]; return label; } /// 文本数字从起始值变化到结束值 - (void)q_countFromValue:(CGFloat)startValue toValue:(CGFloat)endValue { if (self.animationDuration == 0.0f) { self.animationDuration = 2.0f; } [self q_countFromValue:startValue toValue:endValue withDuration:self.animationDuration]; } /// 文本数字在指定时间内从起始值变化到结束值 - (void)q_countFromValue:(CGFloat)startValue toValue:(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 q_setTextValue:endValue]; if (self.completionBlock) { self.completionBlock(); } return; } self.easingRate = 3.0f; self.progress = 0; self.totalTime = duration; self.lastUpdate = [NSDate timeIntervalSinceReferenceDate]; if (self.format == nil) { self.format = @"%f"; } CADisplayLink *timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(q_timerUpdate:)]; timer.frameInterval = 2; [timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; [timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:UITrackingRunLoopMode]; self.timer = timer; } /// 文本数字从当前值变化到结束值 - (void)q_countFromCurrentValueToValue:(CGFloat)endValue { [self q_countFromValue:[self q_getCurrentValue] toValue:endValue]; } /// 文本数字在指定时间内从当前值变化到结束值 - (void)q_countFromCurrentValueToValue:(CGFloat)endValue withDuration:(NSTimeInterval)duration { [self q_countFromValue:[self q_getCurrentValue] toValue:endValue withDuration:duration]; } /// 文本数字从 0 变化到结束值 - (void)q_countFromZeroToValue:(CGFloat)endValue { [self q_countFromValue:0.0f toValue:endValue]; } /// 文本数字在指定时间内从 0 变化到结束值 - (void)q_countFromZeroToValue:(CGFloat)endValue withDuration:(NSTimeInterval)duration { [self q_countFromValue:0.0f toValue:endValue withDuration:duration]; } /// format setter - (void)setFormat:(NSString *)format { _format = format; // update label with new format [self q_setTextValue:self.q_getCurrentValue]; } #pragma mark - 工具方法 /// 定时器定时响应事件处理 - (void)q_timerUpdate:(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 q_setTextValue:[self q_getCurrentValue]]; if (self.progress == self.totalTime) { if (self.completionBlock) { self.completionBlock(); } } } /// 设置数值 - (void)q_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 if (self.positiveFormat.length > 0) { // 带千分位分隔符的浮点型样式 NSString *str = [NSString stringWithFormat:self.format, value]; NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; formatter.numberStyle = NSNumberFormatterDecimalStyle; [formatter setPositiveFormat:self.positiveFormat]; NSString *formatterString = [formatter stringFromNumber:[NSNumber numberWithFloat:[str floatValue]]]; self.text = formatterString; } else { // 普通浮点型样式 self.text = [NSString stringWithFormat:self.format, value]; } } } /// 获取当前值 - (CGFloat)q_getCurrentValue { if (self.progress >= self.totalTime) { return self.destinationValue; } CGFloat percent = self.progress / self.totalTime; CGFloat updateVal = [self update:percent]; return self.startingValue + (updateVal * (self.destinationValue - self.startingValue)); } /// 更新数值 - (CGFloat)update:(CGFloat)t { switch (self.method) { case 0: { int sign = 1; int r = (int)kQLabelCounterRate; if (r % 2 == 0) { sign = -1; } t *= 2; if (t < 1) { return 0.5f * powf(t, kQLabelCounterRate); } else { return sign * 0.5f * (powf(t - 2, kQLabelCounterRate) + sign * 2); } break; } case 1: { return powf(t, kQLabelCounterRate); break; } case 2: { return 1.0 - powf((1.0 - t), kQLabelCounterRate); break; } case 3: { return t; break; } default: return t; } } @end
-
使用
-
1、初始化
-
QCountingLabel 继承自 UILabel, 初始化和 UILabel 一样
// 创建 QCountingLabel 对象 QCountingLabel *countingLabel = [[QCountingLabel alloc] initWithFrame:CGRectMake(0, 0, 300, 120)]; [self.view addSubview:countingLabel]; // 常规设置,QCountingLabel 继承 UILabel, 设置和 UILabel 一样 countingLabel.center = self.view.center; countingLabel.backgroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.5]; countingLabel.font = [UIFont systemFontOfSize:50]; countingLabel.textColor = [UIColor redColor]; countingLabel.textAlignment = NSTextAlignmentCenter;
-
也可以使用类方法一体式创建设置
// 创建 QCountingLabel 对象 QCountingLabel *countingLabel = [QCountingLabel q_countingLabelWithFrame:CGRectMake(50, 100, 300, 120) format:@"%f" positiveFormat:@"###,###.##" method:QCountingMethodEaseOut fromValue:20 toValue:3048.64 withDuration:10.0f completion:^{ NSLog(@"completion"); }]; [self.view addSubview:countingLabel];
-
-
2、设置文本样式
-
不设置时默认为 @"%f"
// 设置文本样式 countingLabel.format = @"%d";
-
也可以使用 block 设置普通样式或富文本样式
// 设置文本样式,使用 block 可以根据不同的值设置多种不同的样式 countingLabel.formatBlock = ^NSString *(CGFloat value) { NSInteger years = value / 12; NSInteger months = (NSInteger)value % 12; if (years == 0) { return [NSString stringWithFormat: @"%ld 个月", (long)months]; } else { return [NSString stringWithFormat: @"%ld 年, %ld 个月", (long)years, (long)months]; } };
-
-
3、设置文本分隔符样式
-
设置金额等金融数字时可以奢姿带有千分位分隔符的浮点数样式
// 设置分隔符样式 countingLabel.positiveFormat = @"###,###.##";
-
-
4、设置文本变化方式
-
不设置时默认为 EaseInOut
// 设置文本变化方式 countingLabel.method = QCountingMethodLinear;
-
-
5、设置变化范围及动画时间
-
不设置时间时默认为 2 秒
[countingLabel q_countFromValue:10 toValue:100]; [countingLabel q_countFromValue:10 toValue:100 withDuration:1.0f];
-
-
6、设置变化完成时的回调
-
数字变化完成时,可以设置回调,再执行其他的操作
// 设置变化完成时的回调 countingLabel.completionBlock = ^void () { NSLog(@"completion"); };
-
-
1.1 整数样式数字的变化
-
创建设置代码
@property (nonatomic, strong) QCountingLabel *countingLabel; // 创建 QCountingLabel 对象 self.countingLabel = [[QCountingLabel alloc] initWithFrame:CGRectMake(0, 0, 300, 120)]; [self.view addSubview:self.countingLabel]; // 常规设置,QCountingLabel 继承 UILabel, 设置和 UILabel 一样 self.countingLabel.center = self.view.center; self.countingLabel.backgroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.5]; self.countingLabel.font = [UIFont systemFontOfSize:50]; self.countingLabel.textColor = [UIColor redColor]; self.countingLabel.textAlignment = NSTextAlignmentCenter; // 设置文本样式 self.countingLabel.format = @"%d"; // 设置变化范围及动画时间 [self.countingLabel q_countFromValue:10 toValue:1000 withDuration:1.0f];
-
效果
-
1.2 浮点数样式数字的变化
-
创建设置代码
@property (nonatomic, strong) QCountingLabel *countingLabel; // 创建 QCountingLabel 对象 self.countingLabel = [[QCountingLabel alloc] initWithFrame:CGRectMake(0, 0, 300, 120)]; [self.view addSubview:self.countingLabel]; // 常规设置,QCountingLabel 继承 UILabel, 设置和 UILabel 一样 self.countingLabel.center = self.view.center; self.countingLabel.backgroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.5]; self.countingLabel.font = [UIFont systemFontOfSize:50]; self.countingLabel.textColor = [UIColor redColor]; self.countingLabel.textAlignment = NSTextAlignmentCenter; // 设置文本样式 self.countingLabel.format = @"%.2f"; // 设置变化范围及动画时间 [self.countingLabel q_countFromValue:0 toValue:3198.23 withDuration:1.0f];
-
效果
-
1.3 带有千分位分隔符的浮点数
-
创建设置代码
@property (nonatomic, strong) QCountingLabel *countingLabel; // 创建 QCountingLabel 对象 self.countingLabel = [[QCountingLabel alloc] initWithFrame:CGRectMake(0, 0, 300, 120)]; [self.view addSubview:self.countingLabel]; // 常规设置,QCountingLabel 继承 UILabel, 设置和 UILabel 一样 self.countingLabel.center = self.view.center; self.countingLabel.backgroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.5]; self.countingLabel.font = [UIFont systemFontOfSize:50]; self.countingLabel.textColor = [UIColor redColor]; self.countingLabel.textAlignment = NSTextAlignmentCenter; // 设置文本样式 self.countingLabel.format = @"%.2f"; // 设置分隔符样式 self.countingLabel.positiveFormat = @"###,###.##"; // 设置变化范围及动画时间 [self.countingLabel q_countFromValue:0 toValue:3048.64 withDuration:1.0f];
-
效果
-