iOS - Label 数字动态变化


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

      Label3

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

      Label4

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

      Label5


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM