新建俩个类:
DNOLabelAnimation : UILabel
DNOSingleText : NSObject
DNOLabelAnimation.h
#import <UIKit/UIKit.h> typedef enum { DNOLabelAnimationTypeNormal = 0, DNOLabelAnimationTypeWave = 1 }DNOLabelAnimationType; @interface DNOLabelAnimation : UILabel @property (nonatomic, copy) NSString *text; @property (nonatomic, copy) NSAttributedString *attributedText; @property (nonatomic, assign) DNOLabelAnimationType type; @property (nonatomic, assign) CGFloat animationHeight; @property (nonatomic, assign) NSUInteger rate; // 1 is fastest 10 is slowest, default is 2 @property (nonatomic, assign) CGFloat kerning; - (instancetype)initWithFrame:(CGRect)frame text:(NSString *)text; - (instancetype)initWithFrame:(CGRect)frame attributedText:(NSAttributedString *)attributedText; - (void)startAnimation; - (void)pauseAnimation; - (void)stopAnimation; @end
DNOLabelAnimation.m
#import "DNOLabelAnimation.h" #import "DNOSingleText.h" //static int loopCount_ = -1; @interface DNOLabelAnimation () @property (nonatomic, strong) NSMutableArray <DNOSingleText *>*allText; @property (nonatomic, strong) CADisplayLink *time; @property (nonatomic, assign) int loopCount; @property (nonatomic, assign) int steps; @end @implementation DNOLabelAnimation @synthesize text = _text; @synthesize attributedText = _attributedText; - (instancetype)initWithCoder:(NSCoder *)coder { self = [super initWithCoder:coder]; if (self) { [self prepare]; [self setNeedsDisplay]; } return self; } - (instancetype)initWithFrame:(CGRect)frame attributedText:(NSAttributedString *)attributedText { self = [super initWithFrame:frame]; if (self) { [self prepare]; self.attributedText = attributedText; } return self; } - (instancetype)initWithFrame:(CGRect)frame text:(NSString *)text { self = [super initWithFrame:frame]; if (self) { [self prepare]; self.text = text; } return self; } - (void)prepare { self.loopCount = -1; } - (void)sizeToFit { [self setFrame:({ CGFloat x = self.frame.origin.x; CGFloat y = self.frame.origin.y; CGFloat width = self.kerning * self.allText.count; CGFloat height = self.font.ascender - self.font.descender + self.animationHeight; CGRectMake(x, y, width, height); })]; } - (void)start { self.steps = 0; self.steps ++; if (self.steps % 3 == 0 || self.steps % 3 == 1 ) { [self setNeedsDisplay]; } if (self.steps > 1000) { self.steps = 0; } } - (void)startAnimation { [self.time invalidate]; self.time = [CADisplayLink displayLinkWithTarget:self selector:@selector(start)]; [self.time addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; } - (void)pauseAnimation { [self.time invalidate]; self.time = nil; } - (void)stopAnimation { [self.time invalidate]; self.time = nil; self.loopCount = -1; [self.allText makeObjectsPerformSelector:@selector(normalReset)]; [self.allText makeObjectsPerformSelector:@selector(waveReset)]; [self setNeedsDisplay]; } - (void)drawRect:(CGRect)rect { __block CGFloat x = 0; __weak typeof(self) weakSelf = self; [self.allText enumerateObjectsUsingBlock:^(DNOSingleText *singleText, NSUInteger idx, BOOL *stop) { CGFloat location = 0; // 仅仅在能第一次遍历玩所有的文字的时候调用 if (self.loopCount == idx * weakSelf.rate) { location = [singleText locationWithFirstEnumerate]; }else{ // 让其他的文字在一开始的时候不要动 location = 0; } // 已经被遍历过的标志 if (singleText.enumerated) { // 判断动画模式 if (weakSelf.type) { location = [singleText locationWithWaveAnimationCompletion:^{ if (idx == weakSelf.allText.count - 1) { [weakSelf.allText makeObjectsPerformSelector:@selector(waveReset)]; self.loopCount = -1; } }]; } else { location = [singleText locationWithNormalAnimation]; } } CGFloat trueLocation = weakSelf.animationHeight - location; CGRect trueRect = CGRectMake(x, trueLocation, weakSelf.textSize, weakSelf.textSize); NSMutableDictionary *attibutes = [NSMutableDictionary dictionary]; attibutes[NSFontAttributeName] = weakSelf.font; attibutes[NSForegroundColorAttributeName] = singleText.textColor; [singleText.text drawInRect:trueRect withAttributes:attibutes]; x += weakSelf.kerning; }]; self.loopCount ++; } #pragma mark - #pragma mark setter && getter - (CGFloat)textSize { return self.font.pointSize + self.animationHeight; } - (void)setText:(NSString *)text { _text = text; [self.allText removeAllObjects]; for (int i = 0; i < text.length; ++i) { DNOSingleText *singleText = [DNOSingleText singleTextWithAnimationRange: text.length]; singleText.text = [text substringWithRange:NSMakeRange(i, 1)]; [self.allText addObject:singleText]; } [self sizeToFit]; [self setNeedsDisplay]; } - (void)setAttributedText:(NSAttributedString *)attributedText { [super setAttributedText:attributedText]; [self.allText removeAllObjects]; for (int i = 0; i < attributedText.length; ++i) { DNOSingleText *singleText = [DNOSingleText singleTextWithAnimationRange: attributedText.length]; singleText.text = [attributedText.string substringWithRange:NSMakeRange(i, 1)]; NSDictionary *attributed = [attributedText attributesAtIndex:i effectiveRange:nil]; singleText.textColor = attributed[NSForegroundColorAttributeName]; [self.allText addObject:singleText]; } [self sizeToFit]; [self setNeedsDisplay]; } - (void)setType:(DNOLabelAnimationType)type { _type = type; self.loopCount = -1; [self.allText makeObjectsPerformSelector:@selector(normalReset)]; [self.allText makeObjectsPerformSelector:@selector(waveReset)]; } - (CGFloat)kerning { return _kerning + self.font.pointSize; } - (NSUInteger)rate { if (!_rate) { _rate = 2; } if (_rate < 1) { _rate = 1; } if (_rate > 10) { _rate = 10; } return _rate; } //- (UIFont *)font //{ // if (![super font]) { // return [UIFont systemFontOfSize:15]; // } // return [super font]; //} @synthesize animationHeight = _animationHeight; - (void)setAnimationHeight:(CGFloat)animationHeight { _animationHeight = animationHeight + self.font.pointSize; } - (CGFloat)animationHeight { if (!_animationHeight) { _animationHeight = self.font.pointSize; } return _animationHeight; } - (NSMutableArray<DNOSingleText *> *)allText { if (!_allText) { _allText = [@[] mutableCopy]; } return _allText; } @end
DNOSingleText.h
#import <UIKit/UIKit.h> @interface DNOSingleText : NSObject @property (nonatomic, copy ) NSString *text; @property (nonatomic, strong) UIColor *textColor; @property (nonatomic, assign) CGFloat animationRange; @property (nonatomic, assign) BOOL enumerated; + (instancetype)singleTextWithAnimationRange:(CGFloat)animationRange; - (CGFloat)locationWithFirstEnumerate; - (CGFloat)locationWithNormalAnimation; - (void)normalReset; - (CGFloat)locationWithWaveAnimationCompletion:(void(^)())completion; - (void)waveReset; @end
DNOSingleText.m
#import "DNOSingleText.h" #define myRandomColor [UIColor colorWithRed:arc4random()%256/255.0 green:arc4random()%256/255.0 blue:arc4random()%256/255.0 alpha:1] @interface DNOSingleText () { CGFloat _normalSteps; CGFloat _waveSteps; BOOL _directionChange; } @end @implementation DNOSingleText + (instancetype)singleTextWithAnimationRange:(CGFloat)animationRange { DNOSingleText *obj = [[self alloc] init]; obj.animationRange = animationRange; return obj; } - (void)calculateStep { _directionChange ? _normalSteps-- : _normalSteps++; if (_normalSteps > self.animationRange) { _directionChange = YES; } else if (_normalSteps < 0) { _directionChange = NO; } } - (CGFloat)locationWithNormalAnimation { [self calculateStep]; return _normalSteps; } - (CGFloat)locationWithFirstEnumerate { [self calculateStep]; _normalSteps += 1; if (_normalSteps > self.animationRange) { _normalSteps = self.animationRange - (_normalSteps - self.animationRange); } self.enumerated = YES; return _normalSteps; } - (CGFloat)locationWithWaveAnimationCompletion:(void (^)())completion { _waveSteps++; if (_waveSteps > 2 * self.animationRange) { completion(); return 0; } return [self locationWithNormalAnimation]; } - (void)normalReset { self.enumerated = NO; _directionChange = NO; _normalSteps = 0; } - (void)waveReset { _waveSteps = 0; [self normalReset]; } @end
然后在需要的地方调用
#import "ViewController.h" #import "DNOLabelAnimation.h" #define myRandomColor [UIColor colorWithRed:arc4random()%256/255.0 green:arc4random()%256/255.0 blue:arc4random()%256/255.0 alpha:1] #define myWidth [UIScreen mainScreen].bounds.size.width #define myHeight [UIScreen mainScreen].bounds.size.height @interface ViewController () @property (nonatomic, strong) NSMutableArray *textViews; @property (nonatomic, assign) DNOLabelAnimationType type; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. [self demo1]; [self demo2]; [self demo3]; [self demo4]; [self demo5]; } - (void)demo1{ NSDictionary *attribute = @{NSForegroundColorAttributeName : myRandomColor}; NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:@"一一一一一一一一一一一一一" attributes:attribute]; [string addAttribute:NSForegroundColorAttributeName value:myRandomColor range:NSMakeRange(2, 2)]; [string addAttribute:NSForegroundColorAttributeName value:myRandomColor range:NSMakeRange(4, 2)]; [string addAttribute:NSForegroundColorAttributeName value:myRandomColor range:NSMakeRange(6, 2)]; [string addAttribute:NSForegroundColorAttributeName value:myRandomColor range:NSMakeRange(8, 2)]; DNOLabelAnimation *textView = [[DNOLabelAnimation alloc] initWithFrame:CGRectMake(20, 60, 0, 0) attributedText:string]; [textView sizeToFit]; [self.view addSubview:textView]; [self.textViews addObject:textView]; [self getButtonWithPoint:CGPointMake(myWidth - 60, 60) tag:1]; } - (void)getButtonWithPoint:(CGPoint)point tag:(NSInteger)tag { UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem]; button.frame = CGRectMake(point.x, point.y, 60, 30); [button addTarget:self action:@selector(buttonDidClick:) forControlEvents:UIControlEventTouchUpInside]; [button setTitle:@"start" forState:UIControlStateNormal]; [button setTitle:@"pause" forState:UIControlStateSelected]; [self.view addSubview:button]; button.tag = tag; } - (void)buttonDidClick:(UIButton *)sender { sender.selected = !sender.selected; if (sender.selected) { [self.textViews[sender.tag - 1] startAnimation]; }else{ [self.textViews[sender.tag - 1] stopAnimation]; //[self.textViews[sender.tag - 1] pauseAnimation]; } } - (NSMutableArray *)textViews { if (!_textViews) { _textViews = [[NSMutableArray alloc] init]; } return _textViews; } - (void)demo2 { NSDictionary *attribute = @{NSForegroundColorAttributeName : myRandomColor}; NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:@"ABCDEFGHIJ" attributes:attribute]; DNOLabelAnimation *textView = [[DNOLabelAnimation alloc] initWithFrame:CGRectMake(20, 120, 0, 0) attributedText:string]; textView.type = self.type; textView.font = [UIFont systemFontOfSize:30]; [textView sizeToFit]; [self.view addSubview:textView]; [self.textViews addObject:textView]; [self getButtonWithPoint:CGPointMake(myWidth - 60, 120) tag:2]; } - (void)demo3 { NSDictionary *attribute = @{NSForegroundColorAttributeName : myRandomColor}; NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:@"abcdefghij" attributes:attribute]; DNOLabelAnimation *textView = [[DNOLabelAnimation alloc] initWithFrame:CGRectMake(20, 180, 0, 0) attributedText:string]; textView.type = self.type; textView.font = [UIFont fontWithName:@"Courier" size:30]; textView.rate = 1; [textView sizeToFit]; [self.view addSubview:textView]; [self.textViews addObject:textView]; [self getButtonWithPoint:CGPointMake(myWidth - 60, 180) tag:3]; } - (void)demo4 { DNOLabelAnimation *textView = [[DNOLabelAnimation alloc] initWithFrame:CGRectMake(20, 240, 0, 0) text:@"黑化肥会挥发"]; textView.type = self.type; textView.font = [UIFont fontWithName:@"Courier" size:30]; textView.rate = 10; [textView sizeToFit]; [self.view addSubview:textView]; [self.textViews addObject:textView]; [self getButtonWithPoint:CGPointMake(myWidth - 60, 240) tag:4]; } - (void)demo5 { DNOLabelAnimation *textView = [[DNOLabelAnimation alloc] initWithFrame:CGRectMake(20, 300, 0, 0) text:@"做人呢,最重要的就是不能让别人开心."]; textView.type = self.type; textView.font = [UIFont systemFontOfSize:10]; textView.kerning = 4; [textView sizeToFit]; [self.view addSubview:textView]; [self.textViews addObject:textView]; [self getButtonWithPoint:CGPointMake(myWidth - 60, 300) tag:5]; }