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