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];-
效果

-
