iOS - Label 数字动态变化

简介: 1、数字动态变化具体实现代码见 GitHub 源码 QExtensionQCountingLabel.h /// 文本数字变化方式枚举 typedef NS_ENUM(NSUInteger, QCountingMethod) { QCountingMetho...

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

目录
相关文章
iOS8下 Xib label高度自适应不换行的解决办法
iOS8下 Xib label高度自适应不换行的解决办法
198 0
|
iOS开发
iOS小技能:自定义tab滑块( segment功能+label混合显示)
iOS小技能:自定义tab滑块( segment功能+label混合显示)
342 0
iOS小技能:自定义tab滑块( segment功能+label混合显示)
|
iOS开发
iOS创建支持长按复制的Label控件
iOS创建支持长按复制的Label控件
271 0
iOS创建支持长按复制的Label控件
|
开发框架 开发工具 iOS开发
iOS开发封装一个可以响应超链接的label——基于RCLabel的交互扩展(二)
iOS开发封装一个可以响应超链接的label——基于RCLabel的交互扩展
226 0
iOS开发封装一个可以响应超链接的label——基于RCLabel的交互扩展(二)
|
iOS开发 UED
iOS开发封装一个可以响应超链接的label——基于RCLabel的交互扩展
iOS开发封装一个可以响应超链接的label——基于RCLabel的交互扩展
287 0
|
网络架构 Swift
swift语言IOS8开发战记12 Font Of Label
       上一章我们实现了对Navigation的格式以及跳转后页面的tableView的设置,但是Cell的显示有一些需要注意的格式问题,比如如果我们要显示的文字过长,之前的做法没有设置,所以多余的文字会被省略掉,还有cell的字体是默认的,我们如何设置字体的格式,也就是font的设置。
967 0
|
2天前
|
Unix 调度 Swift
苹果iOS新手开发之Swift 中获取时间戳有哪些方式?
在Swift中获取时间戳有四种常见方式:1) 使用`Date`对象获取秒级或毫秒级时间戳;2) 通过`CFAbsoluteTimeGetCurrent`获取Core Foundation的秒数,需转换为Unix时间戳;3) 使用`DispatchTime.now()`获取纳秒级精度的调度时间点;4) `ProcessInfo`提供设备启动后的秒数,不表示绝对时间。不同方法适用于不同的精度和场景需求。
13 3
|
1天前
|
Swift iOS开发 Kotlin
苹果iOS新手开发之Swift中实现类似Kotlin的作用域函数
Swift可通过扩展实现类似Kotlin作用域函数效果。如自定义`let`, `run`, `with`, `apply`, `also`,增强代码可读性和简洁性。虽无直接内置支持,但利用Swift特性可达成相似功能。
18 7
|
1天前
|
API 数据处理 开发工具
探索iOS开发的未来:SwiftUI和Combine的融合
【7月更文挑战第23天】随着Apple不断推动其软件开发工具的创新,SwiftUI和Combine框架的出现标志着iOS开发进入了一个新的时代。本文将深入探讨这两个框架如何简化界面设计和事件处理,以及它们如何共同为开发者提供一个更加高效、声明式的编程模型。我们将通过实际示例来展示如何利用SwiftUI构建用户界面,并使用Combine处理异步事件和状态管理。文章还将预测这些技术如何塑造iOS应用开发的未来趋势。