登錄動(dòng)畫

前言

來源于raywenderlich
本文可以看做是入門CoreAnimation的一片文章工禾。其中你會學(xué)習(xí)到
CABasicAnimation CAAnimationGroup CAKeyframeAnimation CASpringAnimation以及部分UIView動(dòng)畫的使用
先看下最終的效果

登錄效果.gif
  • 開始
    開始的樣子應(yīng)該是這樣匾荆,你如果覺得閑太麻煩搔确,可以直接使用xib拖拽,位置大致對齊就可以娃惯,因?yàn)槲覀兊闹攸c(diǎn)是學(xué)習(xí)動(dòng)畫


    初始樣子
  • 接下來是我們第一個(gè)動(dòng)畫---飛入


    飛入

    效果是讓標(biāo)題和用戶名朝巫、密碼的輸入框依次從左邊飛入到屏幕中間
    首先我們要把這三個(gè)控件的橫坐標(biāo)都往左移除一個(gè)屏幕的寬度。


    飛入.png
    CABasicAnimation *flyRightAnimation  = [CABasicAnimation animationWithKeyPath:@"position.x"];
    flyRightAnimation.delegate           = self;
    [flyRightAnimation setValue:@"form" forKey:@"name"];
    [flyRightAnimation setValue:self.headingLabel.layer forKey:@"layer"];
    flyRightAnimation.toValue            = [NSValue valueWithCGPoint: CGPointMake(MainSize.width * 0.5, 0)];
    flyRightAnimation.fromValue          = [NSValue valueWithCGPoint: CGPointMake(-MainSize.width * 0.5, 0)];//-@(MainSize.width * 0.5);
    flyRightAnimation.duration           = .5;
    flyRightAnimation.fillMode           = kCAFillModeBoth;
    [self.headingLabel.layer addAnimation:flyRightAnimation forKey:nil];
    flyRightAnimation.beginTime          = CACurrentMediaTime() + 0.3;
    [flyRightAnimation setValue:self.userNameTextField.layer forKey:@"layer"];
    [self.userNameTextField.layer addAnimation:flyRightAnimation forKey:nil];
    flyRightAnimation.beginTime          = CACurrentMediaTime() + 0.4;
    [flyRightAnimation setValue:self.passWordTextField.layer forKey:@"layer"];
    [self.passWordTextField.layer addAnimation:flyRightAnimation forKey:nil];

通過KeyPath設(shè)置屬性動(dòng)畫還可以是position, bounds, transform 等等石景,設(shè)置了key、value是方便待會動(dòng)畫結(jié)束后的后序動(dòng)畫拙吉,簡單說下屬性:

  • beginTime:設(shè)置動(dòng)畫開始的絕對時(shí)間潮孽,所以我們要先獲取當(dāng)前時(shí)間然后再添加所需的延遲時(shí)間
  • fillMode:這個(gè)屬性控制你動(dòng)畫隊(duì)列的開始和結(jié)束的行為
  • kCAFillModeRemoved是默認(rèn)屬性,他會在動(dòng)畫完成的時(shí)候清除動(dòng)畫做的改變
    kCAFillModeRemoved
  • kCAFillModeBackwards會顯示第一幀筷黔,不管你的動(dòng)畫開始時(shí)間往史,然后在進(jìn)行動(dòng)畫
    kCAFillModeBackwards.png
  • kCAFillModeForwards這是最常用的一種,他會保持動(dòng)畫的最后一幀知道你移除動(dòng)畫佛舱。
    kCAFillModeForwards.png
  • kCAFillModeBoth這是組合了kCAFillModeForwards和kCAFillModeBackwards椎例,你會馬上看到第一幀挨决,當(dāng)動(dòng)畫結(jié)束后悔顯示最后一幀
    Snip20170418_6.png
  • 脈沖動(dòng)畫
    接著剛才我們已經(jīng)把標(biāo)題和輸入框都相繼飛入屏幕中,會有一個(gè)變大的效果订歪。首先我們要等待動(dòng)畫結(jié)束脖祈,因?yàn)槲覀儎偛旁谑褂脛?dòng)畫的時(shí)候已經(jīng)設(shè)置了代理,所以我們只需要在動(dòng)畫結(jié)束的回調(diào)函數(shù)- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;中添加變大效果就可以刷晋。這里我們使用核心動(dòng)畫給的CASpringAnimation
    NSString *name                         = [anim valueForKey:@"name"];
    if ([name isEqualToString:@"form"]) {
        CALayer *layer                     = [anim valueForKey:@"layer"];
        [anim setValue:nil forKey:@"layer"];
        //脈沖動(dòng)畫
        CASpringAnimation *pulseAnimation   = [CASpringAnimation animationWithKeyPath:@"transform.scale"];
        pulseAnimation.damping             = 7.5;
        pulseAnimation.fromValue           = @(1.25);
        pulseAnimation.toValue             = @(1.);
        pulseAnimation.duration            = pulseAnimation.settlingDuration;
        [layer addAnimation:pulseAnimation forKey:nil];
    }

先簡單說下CASpringAnimation的一些屬性:
damping:阻尼和彈簧一樣盖高,在其他因素不變的情況下阻尼越大回復(fù)形變越快
mass:質(zhì)量,在胡克定律中沒有質(zhì)量這個(gè)變量,但是在有阻尼的情況下質(zhì)量會影響彈簧的收縮快慢等眼虱。
stiffness:彈簧的剛度與重量
initialVelocity:初始速度
效果:

脈沖動(dòng)畫

  • 云彩動(dòng)畫
    在標(biāo)題和輸入框都相繼飛入屏幕中的時(shí)候我們需要把背景上的云朵都一個(gè)一個(gè)的顯示出來
 //cloud
    CABasicAnimation *fadeAnimation      = [CABasicAnimation animationWithKeyPath:@"opacity"];
    fadeAnimation.fromValue              = @0.;
    fadeAnimation.toValue                = @1.;
    fadeAnimation.duration               = .5;
    fadeAnimation.fillMode               = kCAFillModeBackwards;
    fadeAnimation.beginTime              = CACurrentMediaTime() + .5;
    [self.cloud1ImageV.layer addAnimation:fadeAnimation forKey:nil];
    fadeAnimation.beginTime              = CACurrentMediaTime() + .7;
    [self.cloud2ImageV.layer addAnimation:fadeAnimation forKey:nil];
    fadeAnimation.beginTime              = CACurrentMediaTime() + .9;
    [self.cloud3ImageV.layer addAnimation:fadeAnimation forKey:nil];
    fadeAnimation.beginTime              = CACurrentMediaTime() + 1.1;
    [self.cloud4ImageV.layer addAnimation:fadeAnimation forKey:nil];

云彩出來了喻奥,還需要讓他們不斷循環(huán)的往右移動(dòng)

CABasicAnimation *moveAnimation   = [CABasicAnimation animationWithKeyPath:@"position.x"];
    [moveAnimation setValue:@"cloud" forKey:@"name"];
    [moveAnimation setValue:layer forKey:@"layer"];
    moveAnimation.delegate            = self;
    moveAnimation.duration            = duration;
    moveAnimation.fillMode            = kCAFillModeForwards;
    moveAnimation.fromValue           = @(layer.position.x);
    moveAnimation.toValue             = @(self.view.ai_width + layer.bounds.size.width * 0.5);
    [layer addAnimation:moveAnimation forKey:nil];
    layer.position                     = CGPointMake(-layer.bounds.size.width/2, layer.position.y);

然后在代理- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag中判斷key,讓他回到屏幕左邊繼續(xù)像右運(yùn)動(dòng)

if ([name isEqualToString:@"cloud"]) {
        CALayer *layer                     = [anim valueForKey:@"layer"];
        layer.position                     = CGPointMake(-layer.bounds.size.width/2, layer.position.y);
        [self animationCloud:layer];
    }

這里需要注意的是捏悬,這兩個(gè)函數(shù)你調(diào)用我撞蚕,我調(diào)用你會形成死循環(huán),導(dǎo)致內(nèi)存泄漏过牙,所以在做動(dòng)畫的前面加一個(gè)全局變量房租內(nèi)存泄漏

 if (!self.isAppear) {//如果沒有顯示直接return防止內(nèi)存泄漏
        return;
    }
  • 登錄按鈕動(dòng)畫
    接下來是組合動(dòng)畫CAAnimationGroup
    組合動(dòng)畫你可以調(diào)整整體的屬性甥厦,如:持續(xù)時(shí)間、代理抒和、動(dòng)畫節(jié)奏等矫渔。
 //loginBtn
    CAAnimationGroup *groupAnimation   = [[CAAnimationGroup alloc]init];
    groupAnimation.beginTime           = CACurrentMediaTime() + .5;
    groupAnimation.duration            = .5;
    groupAnimation.fillMode            = kCAFillModeBackwards;
    groupAnimation.timingFunction      = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
    
    CABasicAnimation *scaleDownAnimation   = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    scaleDownAnimation.fromValue           = @3.5;
    scaleDownAnimation.toValue             = @1.;
    
    CABasicAnimation *rotateAnimation      = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
    rotateAnimation.fromValue              = @M_PI_4;
    rotateAnimation.toValue                = @0;
    
    CABasicAnimation *fadeAnimation        = [CABasicAnimation animationWithKeyPath:@"opacity"];
    fadeAnimation.fromValue                = @0.;
    fadeAnimation.toValue                  = @1.;
    
    groupAnimation.animations              = @[fadeAnimation,scaleDownAnimation,rotateAnimation];
    [self.loginBtn.layer addAnimation:groupAnimation forKey:nil];
     self.loginBtn.alpha                   = 1;

timingFunction:你可以理解為動(dòng)畫的節(jié)奏,下面幾個(gè)常見的值

  • kCAMediaTimingFunctionLinear:以勻速進(jìn)行整個(gè)動(dòng)畫
  • kCAMediaTimingFunctionEaseIn:一開始慢然后快
    kCAMediaTimingFunctionEaseIn
  • kCAMediaTimingFunctionEaseOut:一開始快然后慢
    kCAMediaTimingFunctionEaseOut
  • 點(diǎn)擊登錄動(dòng)畫
    點(diǎn)擊登錄按鈕的時(shí)候登錄按鈕摧莽,需要往下移動(dòng)庙洼,然后變顏色,以及圓角變大镊辕,寬度變寬油够,顯示指示器(也就是菊花)。


    點(diǎn)擊登錄
 //彈簧動(dòng)畫變寬
    AIWeakSelf
    [UIView animateWithDuration:1.5 delay:0. usingSpringWithDamping:.2 initialSpringVelocity:0. options:(UIViewAnimationOptionCurveLinear) animations:^{
        CGRect loginBounds                = self.loginBtn.bounds;
        loginBounds.size.width           += 80;
        weakSelf.loginBtn.bounds          = loginBounds;
    } completion:^(BOOL finished) {
        [weakSelf showMessageWithIndex:0];
        
    }];
    //spinner
    [UIView animateWithDuration:.33 delay:0 usingSpringWithDamping:.7 initialSpringVelocity:0 options:(UIViewAnimationOptionCurveLinear) animations:^{
        weakSelf.loginBtn.ai_centerY         += 60;
        weakSelf.spinner.ai_x                 = 40;
        weakSelf.spinner.alpha                = 1;
        weakSelf.spinner.ai_centerY           = weakSelf.loginBtn.ai_middleY;
    } completion:nil];

這里直接使用UIView動(dòng)畫
duration:動(dòng)畫持續(xù)時(shí)間
delay:動(dòng)畫延遲時(shí)間
dampingRatio:彈簧阻尼
velocity:彈簧速度
options:這里可以選擇動(dòng)畫的節(jié)奏征懈、是否重復(fù)等
animations:動(dòng)畫的回調(diào)
completion:動(dòng)畫完成后的回調(diào)

然后是設(shè)置圓角的背景顏色動(dòng)畫

/**
 動(dòng)畫來改變layer的背景顏色
 
 @param layer   改變的layer
 @param toColor 改變的顏色
 */
- (void)tintBackgroundColorWithCALayer:(CALayer*)layer toColor:(UIColor*)toColor{
    CASpringAnimation *tintAnimation    = [CASpringAnimation animationWithKeyPath:@"backgroundColor"];
    tintAnimation.fromValue            = (__bridge id _Nullable)(layer.backgroundColor);
    tintAnimation.toValue              = (__bridge id _Nullable)(toColor.CGColor);
    tintAnimation.duration             = tintAnimation.settlingDuration;
    tintAnimation.damping              = 7.;
    tintAnimation.mass                 = 10.;
    [layer addAnimation:tintAnimation forKey:nil];
    layer.backgroundColor              = toColor.CGColor;
}

/**
 設(shè)置圓角動(dòng)畫

 @param layer  動(dòng)畫的layer
 @param radius 圓角半徑
 */
- (void)roundCornersWithCALayer:(CALayer*)layer toRadius:(CGFloat)radius {
    CASpringAnimation *radiusAnimation     = [CASpringAnimation animationWithKeyPath:@"cornerRadius"];
    radiusAnimation.fromValue             = @(layer.cornerRadius);
    radiusAnimation.toValue               = @(radius);
    radiusAnimation.duration              = radiusAnimation.settlingDuration;
    radiusAnimation.damping               = 17.;
    [layer addAnimation:radiusAnimation forKey:nil];
    layer.cornerRadius                    = radius;
}

這里都單獨(dú)提出一個(gè)方法來執(zhí)行l(wèi)ayer對應(yīng)的動(dòng)畫石咬,方便以后的項(xiàng)目如果還有這類似的改變顏色或者改變圓角的動(dòng)畫就可以直接把方法復(fù)制過去使用
我們看到中間還會顯示一個(gè)信息條

/**
 顯示一條信息

 @param index 第幾條
 */
- (void)showMessageWithIndex:(NSInteger)index {
    self.label.text  = self.messages[index];
    AIWeakSelf
    [UIView transitionWithView:self.statusImageV duration:.33 options:(UIViewAnimationOptionCurveEaseOut|UIViewAnimationOptionTransitionFlipFromBottom) animations:^{
        weakSelf.statusImageV.hidden = NO;
    } completion:^(BOOL finished) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2. * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            if (index < (weakSelf.messages.count - 1)) {
                [weakSelf removeMessageWithIndex:index];
            }else{
                [weakSelf resetFrom];
            }
        });
    }];
}


/**
 提出一條信息

 @param index 第幾條
 */
- (void)removeMessageWithIndex:(NSInteger)index {
    AIWeakSelf
    [UIView animateWithDuration:.33 animations:^{
        weakSelf.statusImageV.ai_centerX += MainSize.width;
    } completion:^(BOOL finished) {
        weakSelf.statusImageV.hidden      = YES;
        weakSelf.statusImageV.center      = self.statusPoint;
        [weakSelf showMessageWithIndex:index+1];
    }];
}

當(dāng)幾條信息顯示完了后,我們需要重置登錄按鈕狀態(tài)卖哎,這里有個(gè)標(biāo)題有個(gè)搖晃的效果

搖晃

這里我們使用CAKeyframeAnimation

        CAKeyframeAnimation *wobbleAniamtion  = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"];
        wobbleAniamtion.duration              = 0.25;
        wobbleAniamtion.repeatCount           = 4;
        wobbleAniamtion.values                = @[@0.0, @(-M_PI_4), @0.0, @M_PI_4, @0.0];
        wobbleAniamtion.keyTimes              = @[@0.0, @0.25, @0.5, @0.75, @1.0];
        [weakSelf.headingLabel.layer addAnimation:wobbleAniamtion forKey:nil];

以CABasicAnimation一樣的方式創(chuàng)建一個(gè)CAKeyframeAnimation設(shè)置他的重復(fù)次數(shù)鬼悠,持續(xù)是時(shí)間等。
values:動(dòng)畫的關(guān)鍵點(diǎn)亏娜,選擇角度從0°到-45°再到0°再到45°最后回到0°焕窝。(這里起始和結(jié)束一樣可以讓重復(fù)的動(dòng)畫的時(shí)候看上去很自然)
keyTimes:關(guān)鍵時(shí)間,一定要確保你的關(guān)鍵時(shí)間是從0開始到1結(jié)束维贺。
還有些細(xì)節(jié)的動(dòng)畫它掂,由于代碼比較多又是比較重復(fù)的技術(shù)就不貼出來了有興趣可以下載源碼
喜歡的在github上給個(gè)star

源碼位置
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市溯泣,隨后出現(xiàn)的幾起案子虐秋,更是在濱河造成了極大的恐慌榕茧,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件客给,死亡現(xiàn)場離奇詭異用押,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)起愈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門只恨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人抬虽,你說我怎么就攤上這事官觅。” “怎么了阐污?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵休涤,是天一觀的道長。 經(jīng)常有香客問我笛辟,道長功氨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任手幢,我火速辦了婚禮捷凄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘围来。我一直安慰自己跺涤,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布监透。 她就那樣靜靜地躺著桶错,像睡著了一般。 火紅的嫁衣襯著肌膚如雪胀蛮。 梳的紋絲不亂的頭發(fā)上院刁,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機(jī)與錄音粪狼,去河邊找鬼退腥。 笑死,一個(gè)胖子當(dāng)著我的面吹牛再榄,可吹牛的內(nèi)容都是我干的阅虫。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼不跟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了米碰?” 一聲冷哼從身側(cè)響起窝革,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤购城,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后虐译,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瘪板,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年漆诽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了侮攀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,090評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡厢拭,死狀恐怖兰英,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情供鸠,我是刑警寧澤畦贸,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站楞捂,受9級特大地震影響薄坏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜寨闹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一胶坠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧繁堡,春花似錦沈善、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至塑娇,卻和暖如春澈侠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背埋酬。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工哨啃, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人写妥。 一個(gè)月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓拳球,卻偏偏與公主長得像,于是被迫代替她去往敵國和親珍特。 傳聞我的和親對象是個(gè)殘疾皇子祝峻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評論 2 355

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