灰苹果开发买不来的iOS实用技巧ios实用开发技巧

解读RQShineLabel动画效果

2016-11-30  本文已影响293人  H的幻想世界

在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

个人觉得加以修改用作第一次启动程序的动画还是挺炫酷的

上一篇 下一篇

猜你喜欢

热点阅读