在github上看到一个文字闪烁随机渐入渐出的动画效果,跟着写了一遍,把自己对代码的理解加上了注释,发出来希望和大家共同学习,有理解错的地方欢迎指正。
地址 : https://github.com/zipme/RQShineLabel
rqshinelabel.gif#import <UIKit/UIKit.h>
@interface RQShineLabel: UILabel
//渐进时间
@property (assign, nonatomic, readwrite) CFTimeInterval shineDuration;
//渐出时间
@property (assign, nonatomic, readwrite) CFTimeInterval fadeoutDuration;
//开始动画,默认为NO
@property (assign, nonatomic, readwrite, getter = isAutoStart) BOOL autoStart;
//结束动画
@property (assign, nonatomic, readonly, getter = isShining) BOOL shining;
//是否看得到
@property (assign, nonatomic, readonly, getter = isVisible) BOOL visible;
//start the animation
- (void)shine;
- (void)shineWithCompletion:(void(^)())completion;
- (void)fadeOut;
- (void)fadeOutWithCompletion:(void(^)())completion;
#import "RQShineLabel.h"
@interface RQShineLabel()
@property (nonatomic, strong) NSMutableAttributedString *attributedString;
@property (nonatomic, strong) NSMutableArray *characterAnimationDurations;
@property (nonatomic, strong) NSMutableArray *characterAnimationDelays;
@property (nonatomic, strong) CADisplayLink *displayLink;
@property (assign, nonatomic) CFTimeInterval beginTime;
@property (assign, nonatomic) CFTimeInterval endTime;
@property (assign, nonatomic, getter=isFadedOut) BOOL fadedOut;
@property (nonatomic, copy) void (^completion)();
@end
@implementation RQShineLabel
- (instancetype)init
{
self = [super init];
if (!self) {
return nil;
}
[self commonInit];
return self;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (!self) {
return nil;
}
[self commonInit];
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (!self) {
return nil;
}
[self commonInit];
[self setText:self.text];
return self;
}
//公共部分
- (void)commonInit
{
//渐进
_shineDuration = 2.5;
//渐出
_fadeoutDuration = 2.5;
//开启
_autoStart = NO;
//结束
_fadedOut = YES;
//文字颜色
self.textColor = [UIColor whiteColor];
_characterAnimationDurations = [NSMutableArray array];
_characterAnimationDelays = [NSMutableArray array];
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateAttributedString)];
_displayLink.paused = YES;
//添加到运行循环 模式是可以同时做两件事
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
//显示到window时调用
- (void)didMoveToWindow
{
if (nil != self.window && self.autoStart) {
[self shine];
}
}
- (void)setText:(NSString *)text
{
//创建字符属性
self.attributedText = [[NSAttributedString alloc]initWithString:text];
}
- (void)setAttributedText:(NSAttributedString *)attributedText
{
//自定义字符属性
self.attributedString = [self initiaAttributedStringFromAttributedString:attributedText];
//将自定义字符串赋给父类
[super setAttributedText:self.attributedString];
for (NSUInteger i = 0; i < attributedText.length; i ++) {
//随机推迟字符
self.characterAnimationDelays[i] = @(arc4random_uniform(self.shineDuration / 2 * 100) / 100.0);
//还剩下的时间
CGFloat remain = self.shineDuration - [self.characterAnimationDelays[i] floatValue];
//随机消失的时间
self.characterAnimationDurations[i] = @(arc4random_uniform(remain * 100) / 100.0);
}
}
//闪烁
- (void)shine
{
[self shineWithCompletion:NULL];
}
//闪烁 以及 回调
- (void)shineWithCompletion:(void (^)())completion
{
// self.isShining 返回的是 self.displayLink.isPaused的相反值
if (!self.isShining && self.isFadedOut) {
self.completion = completion;
// 每次闪烁开始,就把fadeout 设为 NO
self.fadedOut = NO;
[self startAnimationWithDuration:self.shineDuration];
}
}
- (void)startAnimationWithDuration:(CFTimeInterval)duration
{
//开始的时间 ---》当前时间
self.beginTime = CACurrentMediaTime();
//结束的时间 ---》当前时间 + 2.5
self.endTime = self.beginTime + self.shineDuration;
//动画是否暂停 ---》NO ---》 动画开始
self.displayLink.paused = NO;
}
//淡出
- (void)fadeOut
{
[self fadeOutWithCompletion:NULL];
}
// 再点击屏幕后,(BOOL)isVisible 的返回值为 YES的时候调用
// 初始化显示完全(更新字符状态)self.displayLink.isPaused --》YES
- (void)fadeOutWithCompletion:(void (^)())completion
{
// 非闪烁 and 非动画时间
if (!self.isShining &&!self.isFadedOut) {
//执行BLOCK
self.completion = completion;
//淡出状态
self.fadedOut = YES;
//淡出时间 self.displayLink.isPaused --》NO --》动画开始
[self startAnimationWithDuration:self.fadeoutDuration];
}
}
- (BOOL)isShining
{
//没暂停的时候就闪烁
return !self.displayLink.isPaused;
}
- (BOOL)isVisible
{
//没淡出的时候 就看得见
return NO == self.isFadedOut;
}
//更新字符属性
- (void)updateAttributedString
{
//到现在的时间
CFTimeInterval now = CACurrentMediaTime();
for (NSUInteger i = 0; i < self.attributedString.length; i ++) {
//[NSCharacterSet whitespaceCharacterSet] 删除首尾空格
//如果有当前字符 就返回YES
if ([[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:[self.attributedString.string characterAtIndex:i]]) {
// 从这里,结束 本次循环 ---》 跳出进入下次循环
continue;
}
//指定字符范围执行block设置属性
[self.attributedString enumerateAttribute:NSForegroundColorAttributeName inRange:NSMakeRange(i, 1) options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) {
//获得当前字符的透明度
CGFloat currentAlpha = CGColorGetAlpha([(UIColor *)value CGColor]);
//更新透明度
//淡出并且透明度大于0||不在淡出透明度小于0||调用更新字符属性的时间-动画开始的时间>=当前字符的推迟时间
BOOL shouldUpdateAlpha = (self.isFadedOut && currentAlpha > 0) || (!self.isFadedOut && currentAlpha < 1) || (now - self.beginTime) >= [self.characterAnimationDelays[i] floatValue];
//如果没值 直接返回
if (!shouldUpdateAlpha) {
return ;
}
//两副动画的间隔-推迟的时间/动画随机消失的时间
// now现在的时间不短增加,所以percent会增大,以至于每个字体的颜色透明度增大
// 大于1 作用等同于 1
// 刷新这个 方法 用的link 一分钟60次刷新
CGFloat percentage = (now - self.beginTime - [self.characterAnimationDelays[i] floatValue]) / ([self.characterAnimationDurations[i] floatValue]);
//如果正在淡出
if (self.isFadedOut) {
percentage = 1 - percentage;
}
//获取百分比的透明度
UIColor *color = [self.textColor colorWithAlphaComponent:percentage];
//将透明度赋值给当前字符
[self.attributedString addAttribute:NSForegroundColorAttributeName value:color range:range];
}];
}
// set赋值
[super setAttributedText:self.attributedString];
//如果调用更新字符的时间大于动画结束的时间 动画暂停
if (now > self.endTime){
self.displayLink.paused = YES;
//动画结束时候调用block
if (self.completion) {
self.completion();
}
}
}
- (NSMutableAttributedString *)initiaAttributedStringFromAttributedString:(NSAttributedString *)attributedString
{
//将传进来的自定义字符串转换为可自定义字符串
NSMutableAttributedString *mutableAttributedString = [attributedString mutableCopy];
//文字透明度为0
UIColor *color = [self.textColor colorWithAlphaComponent:0];
//将透明度和字符串的范围赋值给自定义
[mutableAttributedString addAttribute:NSForegroundColorAttributeName value:color range:NSMakeRange(0, mutableAttributedString.length)];
return mutableAttributedString;
}
@end
个人觉得加以修改用作第一次启动程序的动画还是挺炫酷的
网友评论