解讀RQShineLabel動畫效果

在github上看到一個文字閃爍隨機漸入漸出的動畫效果其爵,跟著寫了一遍强胰,把自己對代碼的理解加上了注釋迅箩,發(fā)出來希望和大家共同學習侨歉,有理解錯的地方歡迎指正屋摇。

地址 : 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;
//結(jié)束動畫
@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;
    //結(jié)束
    _fadedOut = YES;
    //文字顏色
    self.textColor = [UIColor whiteColor];
    
    _characterAnimationDurations = [NSMutableArray array];
    _characterAnimationDelays = [NSMutableArray array];
    
    _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateAttributedString)];
   
    _displayLink.paused = YES;
    //添加到運行循環(huán) 模式是可以同時做兩件事
    [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
//顯示到window時調(diào)用
- (void)didMoveToWindow
{
    if (nil != self.window && self.autoStart) {
        [self shine];
    }

}

- (void)setText:(NSString *)text
{
    //創(chuàng)建字符屬性
    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];
}

//閃爍 以及 回調(diào)
- (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();
    //結(jié)束的時間 ---》當前時間 + 2.5
    self.endTime = self.beginTime + self.shineDuration;
    //動畫是否暫停 ---》NO ---》 動畫開始
    self.displayLink.paused = NO;
    
}

//淡出
- (void)fadeOut
{
    [self fadeOutWithCompletion:NULL];

}

// 再點擊屏幕后炮温,(BOOL)isVisible 的返回值為 YES的時候調(diào)用
// 初始化顯示完全(更新字符狀態(tài))self.displayLink.isPaused --》YES
- (void)fadeOutWithCompletion:(void (^)())completion
{
        // 非閃爍 and 非動畫時間
    if (!self.isShining &&!self.isFadedOut) {
        //執(zhí)行BLOCK
        self.completion = completion;
        //淡出狀態(tài)
        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
{
    //到現(xiàn)在的時間
    CFTimeInterval now = CACurrentMediaTime();
    for (NSUInteger i = 0; i < self.attributedString.length; i ++) {
        //[NSCharacterSet whitespaceCharacterSet] 刪除首尾空格
        //如果有當前字符 就返回YES
        if ([[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:[self.attributedString.string characterAtIndex:i]]) {
            // 從這里,結(jié)束 本次循環(huán) ---》 跳出進入下次循環(huán)
            continue;
        }
        //指定字符范圍執(zhí)行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||調(diào)用更新字符屬性的時間-動畫開始的時間>=當前字符的推遲時間
            BOOL shouldUpdateAlpha = (self.isFadedOut && currentAlpha > 0) || (!self.isFadedOut && currentAlpha < 1) || (now - self.beginTime) >= [self.characterAnimationDelays[i] floatValue];
            //如果沒值 直接返回
            if (!shouldUpdateAlpha) {
                return ;
            }
            //兩副動畫的間隔-推遲的時間/動畫隨機消失的時間
            // now現(xiàn)在的時間不短增加牵舵,所以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];
    //如果調(diào)用更新字符的時間大于動畫結(jié)束的時間 動畫暫停
    if (now > self.endTime){
        self.displayLink.paused = YES;
        //動畫結(jié)束時候調(diào)用block
        if (self.completion) {
            self.completion();
        }
    }
}
- (NSMutableAttributedString *)initiaAttributedStringFromAttributedString:(NSAttributedString *)attributedString
{
    //將傳進來的自定義字符串轉(zhuǎn)換為可自定義字符串
    NSMutableAttributedString *mutableAttributedString = [attributedString mutableCopy];
    //文字透明度為0
    UIColor *color = [self.textColor colorWithAlphaComponent:0];
    //將透明度和字符串的范圍賦值給自定義
    [mutableAttributedString addAttribute:NSForegroundColorAttributeName value:color range:NSMakeRange(0, mutableAttributedString.length)];
    
    return mutableAttributedString;
}

@end

個人覺得加以修改用作第一次啟動程序的動畫還是挺炫酷的

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市畸颅,隨后出現(xiàn)的幾起案子白修,更是在濱河造成了極大的恐慌,老刑警劉巖重斑,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兵睛,死亡現(xiàn)場離奇詭異,居然都是意外死亡窥浪,警方通過查閱死者的電腦和手機祖很,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來漾脂,“玉大人假颇,你說我怎么就攤上這事」歉澹” “怎么了笨鸡?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長坦冠。 經(jīng)常有香客問我形耗,道長,這世上最難降的妖魔是什么辙浑? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任激涤,我火速辦了婚禮,結(jié)果婚禮上判呕,老公的妹妹穿的比我還像新娘倦踢。我一直安慰自己送滞,他們只是感情好,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布辱挥。 她就那樣靜靜地躺著犁嗅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪晤碘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天哼蛆,我揣著相機與錄音霞赫,去河邊找鬼。 笑死端衰,一個胖子當著我的面吹牛叠洗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播旅东,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼抵代!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起荤牍,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤案腺,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后康吵,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體劈榨,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡晦嵌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了惭载。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡陡舅,死狀恐怖伴挚,靈堂內(nèi)的尸體忽然破棺而出靶衍,到底是詐尸還是另有隱情,我是刑警寧澤颅眶,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站铡原,受9級特大地震影響商叹,放射性物質(zhì)發(fā)生泄漏燕刻。R本人自食惡果不足惜剖笙,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望弥咪。 院中可真熱鬧,春花似錦酷勺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至片择,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間字管,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工嘲叔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人硫戈。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像汁胆,于是被迫代替她去往敵國和親梭姓。 傳聞我的和親對象是個殘疾皇子嫩码,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容