iOS 数字翻转动画效果

2019-08-26  本文已影响0人  YannChee

动画效果如图:


动画效果
#import "QYScrollableAnimationLabel.h"

@interface QYScrollableAnimationLabel ()
@property(nonatomic, weak) UIView *labelsContainerView;
@property(nonatomic, strong) NSArray *textArr;

@property(nonatomic, copy) NSString *text;
@property(nonatomic, strong) UIFont  *font;
@property(nonatomic, strong) UIColor  *textColor;

@end

@implementation QYScrollableAnimationLabel

+ (instancetype)labelWithText:(nullable NSString *)text font:(nullable UIFont *)font textColor:(nonnull UIColor *)textColor {
    QYScrollableAnimationLabel *labelView = [[QYScrollableAnimationLabel alloc] initWithText:text font:font textColor:textColor];
    return labelView;
}

- (instancetype)initWithText:(nullable NSString *)text font:(nullable UIFont *)font textColor:(nonnull UIColor *)textColor {
    if (self = [super init]) {
        self.font = font;
        self.textColor = textColor;
        self.text = text;
    }
    return self;
}


- (void)setupLabelsContainerFrame {
    UIView *view = self.labelsContainerView;
    
    CGFloat  width = CGRectGetWidth(view.bounds);
    CGFloat  height = CGRectGetHeight(view.bounds);
    CGFloat  x = (CGRectGetWidth(self.bounds) - width) * 0.5;
    CGFloat  y = (CGRectGetHeight(self.bounds) - height) * 0.5;
    view.frame = CGRectMake(x, y, width, height);
}

- (void)playAnimationWithNewText:(NSString *)newText duration:(CFTimeInterval)duration {
    NSArray<NSString *> *oldArr = self.textArr;
    NSArray<NSString *> *newArr = [QYScrollableAnimationLabel charStringArrayWithSourceString:newText];
    // 取出需要做动画的 位数
    NSArray <NSNumber *> *animationPositionArr = [QYScrollableAnimationLabel positionThatNeedAnimationFromNewArray:newArr oldArray:oldArr];
    
    // 遍历,根据位数做动画
    for (NSInteger i = 0; i < self.labelsContainerView.subviews.count; i++) {
        for (NSNumber *num in animationPositionArr) {
            if (num.integerValue == i) {
                [QYScrollableAnimationLabel textChangeAnimationForView:self.labelsContainerView.subviews[i] duration:duration];
            }
        }
    }
    
    // 动画播放完成后赋值
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.text = newText;
    });

}

- (UIView *)labelsContainerWithArray:(NSArray <NSString *>*)arr {
    
    UIView *containerView = ({
        UIView *view = [[UIView alloc] init];
        view;
    });
    
    CGFloat labelsSumWidth = 0.0;
    for (NSInteger i = 0 ; i < arr.count; i++) {
        UILabel *label = ({
            UILabel *label = [[UILabel alloc] init];
            label.font = self.font;
            label.text = arr[i];
            label.textColor = self.textColor;
            [label sizeToFit];
            [containerView addSubview:label];
            label;
        });
        
        // 设置每个label 的frame
        CGFloat x = labelsSumWidth;
        CGFloat y = 0;
        CGFloat width = CGRectGetWidth(label.bounds);
        CGFloat height = CGRectGetHeight(label.bounds);
        label.frame = CGRectMake(x, y, width, height);
        // 累加宽度
        labelsSumWidth += CGRectGetWidth(label.bounds);
    }
    
    // 设置容器的frame
    containerView.bounds = CGRectMake(0, 0, labelsSumWidth, CGRectGetHeight(containerView.subviews.lastObject.bounds));
    
    return containerView;
}

#pragma mark 获取需要播放动画的位数
+ (NSArray <NSNumber *> *)positionThatNeedAnimationFromNewArray:(NSArray<NSString *> *)newArr oldArray:(NSArray<NSString *> *)oldArr {
    // 比较新旧数组位数差异,取出需要播放动画的位数:
    // 1.位数不同,所有位数都做动画
    if (newArr.count != oldArr.count) {
        NSMutableArray *tempArr = [NSMutableArray arrayWithCapacity:oldArr.count];
        [newArr enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            [tempArr addObject:@(idx)];
        }];
        return tempArr;
    }
    // 2.位数相同,取出需要改变的位数
    NSMutableArray *tempArr = [NSMutableArray arrayWithCapacity:oldArr.count];
    for (NSInteger i = 0; i < newArr.count; i++) {
        if (![newArr[i] isEqualToString:oldArr[i]]) {
            [tempArr addObject:@(i)];
        }
    }
    return tempArr;
}

#pragma mark - setter
- (void)setFrame:(CGRect)frame {
    [super setFrame:frame];

    [self setupLabelsContainerFrame];
}

- (void)setText:(NSString *)text {
    _text = text;
    
    [self.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
    UIView *view = ({
        NSArray *arr = [QYScrollableAnimationLabel charStringArrayWithSourceString:text];
        self.textArr = arr;
        UIView *view = [self labelsContainerWithArray:arr];
        [self addSubview:view];
        view;
    });
    self.labelsContainerView = view;
    [self setupLabelsContainerFrame];
    
}

#pragma mark 字符串转数组
+ (NSArray *)charStringArrayWithSourceString:(NSString *)sourceString {
    NSMutableArray<NSString *> *tempArr = [NSMutableArray arrayWithCapacity:sourceString.length];
    for (NSInteger i = 0 ; i < sourceString.length; i++) {
        NSString *subStr = [sourceString substringWithRange:NSMakeRange(i, 1)];
        [tempArr addObject:subStr];
    }
    return tempArr;
}

#pragma mark view做翻转动画
+ (void)textChangeAnimationForView:(UIView *)view duration:(CFTimeInterval)duration {
    CATransition *transition = ({
        CATransition *transition = [[CATransition alloc] init];
        transition.type = kCATransitionPush;
        transition.subtype = kCATransitionFromTop;
        transition.duration = duration;
        transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
        transition;
    });
   
    [view.layer addAnimation:transition forKey:nil];
}

+ (void)textChangeAnimationForView2:(UIView *)view duration:(CFTimeInterval)duration {
    CATransition *transition = ({
        CATransition *transition = [[CATransition alloc] init];
        transition.type = kCATransitionPush;
        transition.subtype = kCATransitionFromTop;
        transition.duration = duration;
        transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
        transition;
    });
    
    [view.layer addAnimation:transition forKey:nil];
}

@end

上一篇 下一篇

猜你喜欢

热点阅读