| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- //
- // MSNumberScrollAnimatedView.m
- // MSNumberScrollAnimatedViewDemo
- //
- // Created by MrSong on 16/5/20.
- // Copyright © 2016年 MrSong. All rights reserved.
- //
- #import "MSNumberScrollAnimatedView.h"
- @interface MSNumberScrollAnimatedView ()
- @property (nonatomic, strong) NSMutableArray *numbersText; //保存拆分出来的数字
- @property (nonatomic, strong) NSMutableArray *scrollLayers;
- @property (nonatomic, strong) NSMutableArray *scrollViews; // 保存imageView
- @property (nonatomic, strong) NSNumber *previousNumber; // 保存上一次的数字
- @property (nonatomic, copy) NSArray *numberImages;
- /** vipType 类型 */
- @property (nonatomic, assign) NSInteger vipType;
- @end
- @implementation MSNumberScrollAnimatedView
- #pragma mark - Life Cycle
- - (instancetype)initWithVipType:(NSInteger)vipType {
- if (self = [super init]) {
- _vipType = vipType;
- [self commonInit];
- }
- return self;
- }
- - (void)layoutSubviews {
- [super layoutSubviews];
-
- // 如果已经设置了number但还没有准备动画,在布局完成后准备
- if (_number && _scrollLayers.count == 0) {
- [self prepareAnimations];
- }
- }
- #pragma mark - Public Methods
- - (void)reloadView {
- [self prepareAnimations];
- }
- - (void)startAnimation {
- [self createAnimations];
- }
- - (void)stopAnimation {
- for (CALayer *layer in _scrollLayers) {
- [layer removeAnimationForKey:@"MSNumberScrollAnimatedView"];
- }
- }
- #pragma mark - Private Methods
- - (void)commonInit {
- self.duration = 0.1;
- self.durationOffset = 0.1;
- self.density = 1;
- self.minLength = 0;
- self.isAscending = NO;
-
- _numbersText = [NSMutableArray array];
- _scrollLayers = [NSMutableArray array];
- _scrollViews = [NSMutableArray array];
- _previousNumber = nil;
-
- if (self.vipType > 0) {
- self.numberImages = @[
- [UIImage imageNamed:@"gift_vip_num_0"],
- [UIImage imageNamed:@"gift_vip_num_1"],
- [UIImage imageNamed:@"gift_vip_num_2"],
- [UIImage imageNamed:@"gift_vip_num_3"],
- [UIImage imageNamed:@"gift_vip_num_4"],
- [UIImage imageNamed:@"gift_vip_num_5"],
- [UIImage imageNamed:@"gift_vip_num_6"],
- [UIImage imageNamed:@"gift_vip_num_7"],
- [UIImage imageNamed:@"gift_vip_num_8"],
- [UIImage imageNamed:@"gift_vip_num_9"],
- ];
- } else {
- self.numberImages = @[
- [UIImage imageNamed:@"gift_common_num_0"],
- [UIImage imageNamed:@"gift_common_num_1"],
- [UIImage imageNamed:@"gift_common_num_2"],
- [UIImage imageNamed:@"gift_common_num_3"],
- [UIImage imageNamed:@"gift_common_num_4"],
- [UIImage imageNamed:@"gift_common_num_5"],
- [UIImage imageNamed:@"gift_common_num_6"],
- [UIImage imageNamed:@"gift_common_num_7"],
- [UIImage imageNamed:@"gift_common_num_8"],
- [UIImage imageNamed:@"gift_common_num_9"],
- ];
- }
-
- // 添加布局完成后的回调
- [self setNeedsLayout];
- [self layoutIfNeeded];
- }
- - (void)prepareAnimations {
- // 先删除旧数据
- for (CALayer *layer in _scrollLayers) {
- [layer removeFromSuperlayer];
- }
- [_numbersText removeAllObjects];
- [_scrollLayers removeAllObjects];
- [_scrollViews removeAllObjects];
-
- // 配置新的数据和UI
- [self configNumbersText];
- [self configScrollLayers];
- }
- - (void)configNumbersText {
- NSString *numberStr = [_number stringValue];
- // 如果 number 长度小于 最小长度就补0
- // 这里需要注意一下 minLength 和 length 都是NSUInteger类型 如果相减得负数的话会有问题
- for (NSInteger i = 0; i < (NSInteger)self.minLength - (NSInteger)numberStr.length; i++) {
- [_numbersText addObject:@"0"];
- }
- // 取出 number 各位数
- for (NSUInteger i = 0; i < numberStr.length; i++) {
- [_numbersText addObject:[numberStr substringWithRange:NSMakeRange(i, 1)]];
- }
- }
- - (void)configScrollLayers {
- // 确保视图有宽度
- if (CGRectGetWidth(self.frame) <= 0) {
- return;
- }
-
- // 平均分配宽度
- CGFloat width = CGRectGetWidth(self.frame) / _numbersText.count;
- CGFloat height = CGRectGetHeight(self.frame);
- // 创建和配置 scrollLayer
- for (NSUInteger i = 0; i < _numbersText.count; i++) {
- CAScrollLayer *layer = [CAScrollLayer layer];
- layer.frame = CGRectMake(i*width, 0, width, height);
- [_scrollLayers addObject:layer];
- [self.layer addSublayer:layer];
-
- NSString *numberText = _numbersText[i];
- [self configScrollLayer:layer numberText:numberText];
- }
- }
- - (void)configScrollLayer:(CAScrollLayer *)layer numberText:(NSString *)numberText {
- NSInteger number = [numberText integerValue];
- NSMutableArray *scrollNumbers = [NSMutableArray array];
- for (NSInteger i = 0; i < self.density + 1; i++) {
- [scrollNumbers addObject:[NSString stringWithFormat:@"%u", (unsigned int)((number+i) % 10)]];
- }
- [scrollNumbers addObject:numberText];
-
- __block CGFloat height = 0;
- [scrollNumbers enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSString *text, NSUInteger idx, BOOL * _Nonnull stop) {
- UIImageView *imageView = [self createImageViewForNumber:[text integerValue]];
-
- // 设置固定的显示大小
- CGFloat width = CGRectGetWidth(layer.frame);
- CGFloat displayHeight = CGRectGetHeight(layer.frame);
-
- // 确保图片完全填充可用空间
- imageView.frame = CGRectMake(0, height, width, displayHeight);
- imageView.contentMode = UIViewContentModeScaleAspectFit;
-
- [layer addSublayer:imageView.layer];
- [_scrollViews addObject:imageView];
- height = CGRectGetMaxY(imageView.frame);
- }];
- }
- - (UIImageView *)createImageViewForNumber:(NSInteger)number {
- UIImageView *imageView = [[UIImageView alloc] init];
- imageView.contentMode = UIViewContentModeScaleAspectFit;
-
- if (self.numberImages && number >= 0 && number < self.numberImages.count) {
- imageView.image = self.numberImages[number];
- }
-
- return imageView;
- }
- - (void)createAnimations {
- NSString *currentNumberStr = [_number stringValue];
- NSString *previousNumberStr = [_previousNumber stringValue];
-
- // 补齐前导零,使两个数字字符串长度相同
- while (currentNumberStr.length < self.minLength) {
- currentNumberStr = [@"0" stringByAppendingString:currentNumberStr];
- }
- while (previousNumberStr.length < self.minLength) {
- previousNumberStr = [@"0" stringByAppendingString:previousNumberStr];
- }
-
- // 第一个需要动画的layer的动画持续时间
- NSTimeInterval duration = self.duration - ((_numbersText.count-1) * self.durationOffset);
-
- for (NSUInteger i = 0; i < _scrollLayers.count; i++) {
- CALayer *layer = _scrollLayers[i];
-
- // 检查当前位置的数字是否发生变化
- BOOL shouldAnimate = YES;
- if (_previousNumber != nil) {
- NSInteger currentIndex = currentNumberStr.length - _scrollLayers.count + i;
- NSInteger previousIndex = previousNumberStr.length - _scrollLayers.count + i;
-
- if (currentIndex >= 0 && previousIndex >= 0) {
- unichar currentDigit = [currentNumberStr characterAtIndex:currentIndex];
- unichar previousDigit = [previousNumberStr characterAtIndex:previousIndex];
- shouldAnimate = (currentDigit != previousDigit);
- }
- }
-
- if (shouldAnimate) {
- CGFloat maxY = [[layer.sublayers lastObject] frame].origin.y;
- CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"sublayerTransform.translation.y"];
- animation.duration = duration;
- animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
-
- if (self.isAscending) {
- animation.fromValue = @0;
- animation.toValue = [NSNumber numberWithFloat:-maxY];
- } else {
- animation.fromValue = [NSNumber numberWithFloat:-maxY];
- animation.toValue = @0;
- }
-
- [layer addAnimation:animation forKey:@"MSNumberScrollAnimatedView"];
- }
-
- duration += self.durationOffset;
- }
- }
- #pragma mark - Setter
- - (void)setNumber:(NSNumber *)number {
- _previousNumber = _number; // 保存当前数字作为上一次的数字
- _number = number;
- // 准备动画
- [self prepareAnimations];
- }
- @end
|