.h文件
#import <UIKit/UIKit.h>
@interface HAScrollNumberLabel : UIView
/// size改變回調(diào)
@property (nonatomic, copy) void(^didChangeSizeHandler)(CGSize size);
/// 當(dāng)前顯示值
@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
/// 目標(biāo)顯示值
@property (nonatomic, assign) NSInteger targetNumber;
/// 改變的數(shù)值
@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 ()
/// 當(dāng)前顯示值
@property (nonatomic, strong, readwrite) NSNumber *currentNumber;
/// 目標(biāo)顯示值
@property (nonatomic, strong) NSNumber *targetNumber;
/// 標(biāo)簽數(shù)組
@property (nonatomic, strong) NSMutableArray<UILabel *> *cellArray;
/// 當(dāng)前列數(shù)
@property (nonatomic, assign) NSUInteger rowNumber;
/// 最大列數(shù),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;
/// 任務(wù)數(shù)組
@property (nonatomic, strong) NSMutableArray *taskQueue;
/// 動(dòng)畫(huà)計(jì)數(shù)
@property (nonatomic, assign) NSInteger finishedAnimationCount;
/// 是否正在動(dòng)畫(huà)
@property (nonatomic, assign) BOOL isAnimating;
/// 當(dāng)前的寬度
@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;
}
/// 改變顯示值,滾動(dòng)動(dòng)畫(huà)的間隔會(huì)自動(dòng)計(jì)算
- (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<NSNumber *> *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);
}
/// 更新行數(shù)
- (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;
}
/// 檢查動(dòng)畫(huà)
- (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];
}
}
}
}
/// 改變動(dòng)畫(huà)
- (void)playAnimationWithChange:(NSInteger)changeValue previousNumber:(NSNumber *)previousNumber {
NSInteger targetInteger = _targetNumber.integerValue;
NSInteger targetRowNumber = [self calculateNumberRow: targetInteger];
if (targetRowNumber > _rowNumber) {
[self updateToRowNumber: targetRowNumber];
}
NSArray<NSNumber *> *repeatCountArray = [self getRepeatTimesWithChangeNumber: changeValue targetNumber: targetInteger];
NSArray<NSNumber *> *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)];
}
}
}
}
/// 單列動(dòng)畫(huà)
- (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];
}];
}
/// 多列動(dòng)畫(huà)
- (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];
}];
}];
}
/// 動(dòng)畫(huà)結(jié)束
- (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);
}
}
}
/// 計(jì)算列數(shù)
- (NSInteger)calculateNumberRow:(NSInteger)number {
NSInteger numberRow = 1;
while ((number = number / 10) != 0) {
numberRow++;
}
return numberRow;
}
/// 移動(dòng)數(shù)值
- (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);
}
/// 滾動(dòng)次數(shù)
- (NSArray<NSNumber *> *)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;
}
/// 顯示數(shù)組
- (NSArray<NSNumber *> *)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;
}
/// 獲取滾動(dòng)時(shí)間
- (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;
}
/// 標(biāo)簽
- (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
最后編輯于 :
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。