iOS 滚动数字

.h文件

#import 

@interface HAScrollNumberLabel : UIView

/// size改变回调
@property (nonatomic, copy) void(^didChangeSizeHandler)(CGSize size);

/// 当前显示值
@property (nonatomic, strong, readonly) NSNumber *currentNumber;

/// 初始化
- (instancetype)initWithNumber:(NSNumber *)number font:(UIFont *)font textColor:(UIColor *)textColor rowNumber:(NSUInteger)rowNumber;

/// 改变显示值
- (void)changeToNumber:(NSNumber *)number animated:(BOOL)animated;

@end
.m文件

#import "HAScrollNumberLabel.h"

@interface HAAnimationTask : NSObject

/// 目标显示值
@property (nonatomic, assign) NSInteger targetNumber;

/// 改变的数值
@property (nonatomic, assign) NSInteger changeValue;

@end

@implementation HAAnimationTask

@end

static const NSUInteger numberCellLineCount = 21;

static NSString * const numberCellText = @"0\n9\n8\n7\n6\n5\n4\n3\n2\n1\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n0";

@interface HAScrollNumberLabel ()

/// 当前显示值
@property (nonatomic, strong, readwrite) NSNumber *currentNumber;

/// 目标显示值
@property (nonatomic, strong) NSNumber *targetNumber;

/// 标签数组
@property (nonatomic, strong) NSMutableArray *cellArray;

/// 当前列数
@property (nonatomic, assign) NSUInteger rowNumber;

/// 最大列数,10位
@property (nonatomic, assign) NSUInteger maxRowNumber;

/// 最小列表
@property (nonatomic, assign) NSInteger minRowNumber;

/// 列宽
@property (nonatomic, assign) CGFloat cellWidth;

/// 行高
@property (nonatomic, assign) CGFloat numberCellHeight;

/// 字体颜色
@property (nonatomic, strong) UIColor *textColor;

/// 字体
@property (nonatomic, strong) UIFont *font;

/// 任务数组
@property (nonatomic, strong) NSMutableArray *taskQueue;

/// 动画计数
@property (nonatomic, assign) NSInteger finishedAnimationCount;

/// 是否正在动画
@property (nonatomic, assign) BOOL isAnimating;

/// 当前的宽度
@property (nonatomic, assign) CGFloat totalWidth;

@end

@implementation HAScrollNumberLabel

/// 初始化
- (instancetype)initWithNumber:(NSNumber *)number font:(UIFont *)font textColor:(UIColor *)textColor rowNumber:(NSUInteger)rowNumber {
    self = [super init];
    if (self) {
        
        CGSize size = [numberCellText boundingRectWithSize: CGSizeZero options: NSStringDrawingUsesLineFragmentOrigin attributes: @{
            NSFontAttributeName: font
        } context: nil].size;
        
        _cellWidth = size.width;
        _numberCellHeight = size.height;
        _targetNumber = number;
        _currentNumber = number;
        _font = font;
        _textColor = textColor;
        _isAnimating = NO;
        _finishedAnimationCount = 0;
        _maxRowNumber = 10;
        if (rowNumber > 0 && rowNumber <= _maxRowNumber) {
            _rowNumber = rowNumber;
        } else {
            _rowNumber = [self calculateNumberRow: number.integerValue];
        }
        _minRowNumber = rowNumber;
        _cellArray = [[NSMutableArray alloc] init];
        
        NSArray *displayNumberArray = [self getEachCellValueArrayWithTargetNumber: number.integerValue];
        
        for (NSInteger i = 0; i < _rowNumber; i++) {
            UILabel *numberCell = [self makeNumberCell];
            numberCell.frame = CGRectMake((_rowNumber - 1 - i) * _cellWidth, 0, _cellWidth, _numberCellHeight);
            NSNumber *displayNum = [displayNumberArray objectAtIndex: i];
            [self moveNumberCell: numberCell toNumber: displayNum.integerValue];
            [self addSubview: numberCell];
            [_cellArray addObject: numberCell];
        }
        
        self.bounds = CGRectMake(0, 0, _rowNumber * _cellWidth, _numberCellHeight / numberCellLineCount);
        self.backgroundColor = [UIColor clearColor];
        self.layer.masksToBounds = YES;
    }
    return self;
}

/// 改变显示值,滚动动画的间隔会自动计算
- (void)changeToNumber:(NSNumber *)number animated:(BOOL)animated {
    if ([self calculateNumberRow: number.integerValue] > _maxRowNumber) {
        return;
    }
    if (number.integerValue == _currentNumber.integerValue) {
        return;
    }
    if (_isAnimating) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            HAAnimationTask *task = [[HAAnimationTask alloc] init];
            task.targetNumber = number.integerValue;
            task.changeValue = number.integerValue - self.targetNumber.integerValue;
            @synchronized (self.taskQueue) {
                [self.taskQueue removeAllObjects];
                [self.taskQueue addObject: task];
            }
        });
    } else {
        NSNumber *previousNumber = _targetNumber;
        _targetNumber = number;
        if (animated) {
            [self playAnimationWithChange: number.integerValue - previousNumber.integerValue previousNumber: previousNumber];
            _isAnimating = YES;
        } else {
            NSArray *displayNumbers = [self getEachCellValueArrayWithTargetNumber: number.integerValue];
            for (int i = 0; i < displayNumbers.count; i++) {
                [self moveNumberCell: _cellArray[i] toNumber: displayNumbers[i].integerValue];
            }
        }
    }
    self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, _rowNumber * _cellWidth, _numberCellHeight / numberCellLineCount);
}

/// 更新行数
- (void)updateToRowNumber:(NSInteger)rowNumber {
    if (rowNumber == _rowNumber || rowNumber < _minRowNumber) {
        return;
    }
    /// 移除全部
    [_cellArray makeObjectsPerformSelector: @selector(removeFromSuperview)];
    if (rowNumber > _rowNumber) {
        for (NSInteger i = _rowNumber; i < rowNumber; i++) {
            UILabel *scrollCell = [self makeNumberCell];
            scrollCell.frame = CGRectMake((_rowNumber - 1 - i) * _cellWidth, -_numberCellHeight * 10 / numberCellLineCount, _cellWidth, _numberCellHeight);
            [_cellArray addObject: scrollCell];
        }
    } else {
        for (NSInteger i = rowNumber; i < _rowNumber; i++) {
            [_cellArray removeLastObject];
        }
    }
    /// 重新添加
    for (UILabel *cell in _cellArray) {
        [self addSubview: cell];
    }
    for (int i = 0; i < rowNumber; i++) {
        UILabel *cell = [_cellArray objectAtIndex: i];
        cell.frame = CGRectMake((rowNumber - 1 - i) * _cellWidth, cell.frame.origin.y, _cellWidth, _numberCellHeight);
    }
    self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, rowNumber * _cellWidth, _numberCellHeight / numberCellLineCount);
    _rowNumber = rowNumber;
}

/// 检查动画
- (void)checkTaskArray {
    @synchronized (self.taskQueue) {
        if (self.taskQueue.count != 0) {
            HAAnimationTask *task = [self.taskQueue firstObject];
            [self.taskQueue removeObject: task];
            NSNumber *previousNumber = self.targetNumber;
            self.targetNumber = @(task.targetNumber);
            [self playAnimationWithChange: task.changeValue previousNumber: previousNumber];
        } else {
            self.isAnimating = NO;
            NSInteger needNumberRow = [self calculateNumberRow: self.currentNumber.intValue];
            if (needNumberRow < self.rowNumber) {
                [self updateToRowNumber: needNumberRow];
            }
        }
    }
}

/// 改变动画
- (void)playAnimationWithChange:(NSInteger)changeValue previousNumber:(NSNumber *)previousNumber {
    
    NSInteger targetInteger = _targetNumber.integerValue;
    NSInteger targetRowNumber = [self calculateNumberRow: targetInteger];
    
    if (targetRowNumber > _rowNumber) {
        [self updateToRowNumber: targetRowNumber];
    }
    
    NSArray *repeatCountArray = [self getRepeatTimesWithChangeNumber: changeValue targetNumber: targetInteger];
    NSArray *targetDisplayNums = [self getEachCellValueArrayWithTargetNumber: targetInteger];
    
    CGFloat interval = [self getIntervalWithPreviousNumber: previousNumber.integerValue targetNumber: targetInteger];
    
    if (repeatCountArray.count != 0) {
        for (NSInteger i = 0; i < repeatCountArray.count; i++) {
            NSInteger willDisplayNum = [targetDisplayNums objectAtIndex: i].integerValue;
            UILabel *cell = [_cellArray objectAtIndex: i];
            if ([repeatCountArray objectAtIndex: i].integerValue == 0) {
                [self makeSingleAnimationWithCell: cell animationCount: repeatCountArray.count displayNumber: willDisplayNum duration: interval];
            } else {
                CGFloat duration = interval * (10 - [self getValueOfCell: cell]) / ceilf(fabs(changeValue / pow(10, i)));
                [self makeMultiAnimationWithCell: cell animationCount: repeatCountArray.count displayNumber: willDisplayNum duration: duration delay: MAX(0, interval - duration)];
            }
        }
    }
}

/// 单列动画
- (void)makeSingleAnimationWithCell:(UILabel *)cell animationCount:(NSInteger)count  displayNumber:(NSInteger)displayNumber duration:(CGFloat)duration {
    [UIView animateWithDuration: duration delay: 0.0 options: UIViewAnimationOptionCurveEaseOut animations:^{
        [self moveNumberCell: cell toNumber: displayNumber];
    } completion:^(BOOL finished) {
        [self oneAnimationDidFinishedWithTotalCount: count];
    }];
}

/// 多列动画
- (void)makeMultiAnimationWithCell:(UILabel *)cell animationCount:(NSInteger)count displayNumber:(NSInteger)displayNumber duration:(NSTimeInterval)duration delay:(NSTimeInterval)delay {
    [UIView animateWithDuration: duration delay: 0.0 options: UIViewAnimationOptionCurveEaseIn animations:^{
        [self moveNumberCell: cell toNumber: 10];
    } completion:^(BOOL finished) {
        [self moveNumberCell: cell toNumber: 0];
        [UIView animateWithDuration: delay delay: 0 options: UIViewAnimationOptionCurveEaseOut animations:^{
            [self moveNumberCell: cell toNumber: displayNumber];
        } completion:^(BOOL finished) {
            [self oneAnimationDidFinishedWithTotalCount: count];
        }];
    }];
}

/// 动画结束
- (void)oneAnimationDidFinishedWithTotalCount:(NSInteger)totalCount {
    _finishedAnimationCount++;
    if (_finishedAnimationCount == totalCount) {
        _finishedAnimationCount = 0;
        _currentNumber = _targetNumber;
        [self checkTaskArray];
    }
}

/// size改变
- (void)setFrame:(CGRect)frame {
    [super setFrame:frame];
    if (_totalWidth != frame.size.width) {
        _totalWidth = frame.size.width;
        if (_didChangeSizeHandler) {
            _didChangeSizeHandler(frame.size);
        }
    }
}

/// 计算列数
- (NSInteger)calculateNumberRow:(NSInteger)number {
    NSInteger numberRow = 1;
    while ((number = number / 10) != 0) {
        numberRow++;
    }
    return numberRow;
}

/// 移动数值
- (void)moveNumberCell:(UILabel *)cell toNumber:(NSInteger)number {
    CGFloat x = cell.frame.origin.x;
    CGFloat floatNumber = abs((int)number);
    CGFloat y = -_numberCellHeight / numberCellLineCount * 10 - ((CGFloat)floatNumber / numberCellLineCount) * _numberCellHeight;
    cell.frame = CGRectMake(x, y, _cellWidth, _numberCellHeight);
}

/// 滚动次数
- (NSArray *)getRepeatTimesWithChangeNumber:(NSInteger)change targetNumber:(NSInteger)targetNumber {
    NSMutableArray *repeatTimesArray = [[NSMutableArray alloc] init];
    NSInteger originNumber = targetNumber - change;
    if (change > 0) {
        do {
            targetNumber = (targetNumber / 10) * 10;
            originNumber = (originNumber / 10) * 10;
            NSNumber *repeat = @((targetNumber - originNumber) / 10);
            [repeatTimesArray addObject: repeat];
            targetNumber = targetNumber / 10;
            originNumber = originNumber / 10;
        } while ((targetNumber - originNumber) != 0);
    } else {
        do {
            targetNumber = (targetNumber / 10) * 10;
            originNumber = (originNumber / 10) * 10;
            NSNumber *repeat = @((originNumber - targetNumber) / 10);
            [repeatTimesArray addObject: repeat];
            targetNumber = targetNumber / 10;
            originNumber = originNumber / 10;
        } while ((originNumber - targetNumber) != 0);
    }
    return repeatTimesArray;
}

/// 显示数组
- (NSArray *)getEachCellValueArrayWithTargetNumber:(NSInteger)targetNumber {
    NSMutableArray *cellValueArray = [[NSMutableArray alloc] init];
    NSInteger tmp;
    for (NSInteger i = 0; i < _rowNumber; i++) {
        tmp = targetNumber % 10;
        NSNumber *number = @(tmp);
        [cellValueArray addObject:number];
        targetNumber = targetNumber / 10;
    }
    return cellValueArray;
}

/// 获取显示值
- (NSInteger)getValueOfCell:(UILabel *)cell {
    CGFloat y = cell.frame.origin.y;
    CGFloat tmpNumber = (- (y * numberCellLineCount / _numberCellHeight)) - 10;
    NSInteger displayNumber = (NSInteger)roundf(tmpNumber);
    displayNumber = abs((int)displayNumber);
    return displayNumber;
}

/// 获取滚动时间
- (CGFloat)getIntervalWithPreviousNumber:(NSInteger)previousNumber targetNumber:(NSInteger)targetNumber {
    NSArray *repeatTimesArray = [self getRepeatTimesWithChangeNumber: targetNumber - previousNumber targetNumber: targetNumber];
    NSUInteger count = repeatTimesArray.count;
    NSInteger tmp1 = targetNumber / (NSInteger)pow(10, count - 1);
    NSInteger tmp2 = previousNumber / (NSInteger)pow(10, count - 1);
    NSInteger maxChangeNum = labs(tmp1 % 10 - tmp2 % 10);
    return 0.2 * count * maxChangeNum;
}

/// 标签
- (UILabel *)makeNumberCell {
    UILabel *cell = [[UILabel alloc] init];
    cell.font = _font;
    cell.numberOfLines = numberCellLineCount;
    cell.textColor = _textColor;
    cell.text = numberCellText;
    cell.textAlignment = NSTextAlignmentCenter;
    return  cell;
}

- (NSMutableArray *)taskQueue {
    if (!_taskQueue) {
        _taskQueue = [NSMutableArray arrayWithCapacity: 1];
    }
    return _taskQueue;
}

@end

你可能感兴趣的:(iOS 滚动数字)