动画效果如图:
动画效果
#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








网友评论