iOS動畫-Transform和KeyFrame動畫

序言

追求美好是人的天性因谎,這是猿們無法避免的亏推。我們總是追求更為酷炫的實現(xiàn),如果足夠仔細叁巨,我們不難發(fā)現(xiàn)一個好的動畫通過步驟分解后本質上不過是一個個簡單的動畫實現(xiàn)斑匪,正是這些基本的動畫在經過合理的搭配組合后化腐朽為神奇,令人驚艷锋勺。因此蚀瘸,掌握最基本的動畫是完成酷炫開發(fā)之旅的根本。

作為動畫篇的第二篇文章宙刘,我在從UIView動畫說起簡單介紹了關于UIView的幾種基本動畫苍姜,這幾種動畫的搭配讓我們的登錄界面富有靈性生動,但是這幾種動畫總是無法滿足我們對于動畫的需求悬包。同樣的衙猪,本文將從一個小demo開始講解強大的transform動畫以及關鍵幀keyFrame動畫。

demo動效圖

可以看到兩個動畫:葉子被風吹落以及左邊的文字從summer變化到autumn布近,這兩個動畫都是基于強大的transform形變垫释,其中葉子的飄落動畫通過關鍵幀動畫實現(xiàn)。demo鏈接

transform動畫

transform是一個非常重要的屬性撑瞧,它在矩陣變換的層面上改變視圖的顯示效果棵譬,完成旋轉、形變预伺、平移等等操作订咸。在它被修改的同時曼尊,視圖的frame也會被真實改變。有兩個數(shù)據(jù)類型用來表示transform脏嚷,分別是CGAffineTransformCATransform3D骆撇。前者作用于UIView,后者為layer層次的變換類型父叙∩窠迹基于后者可以實現(xiàn)更加強大的功能,但我們需要先掌握CGAffineTransform類型的使用趾唱。同時涌乳,本文講解也是這個變換類型。

對于想要了解矩陣變換是如何作用實現(xiàn)的甜癞,可以參考這篇博客:CGAffineTransform 放射變換

talk is cheap show you the code

在開始使用transform實現(xiàn)你的動畫之前夕晓,我先介紹幾個常用的函數(shù):

/// 用來連接兩個變換效果并返回。返回的t = t1 * t2
CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2)

/// 矩陣初始值带欢。[ 1 0 0 1 0 0 ]
CGAffineTransformIdentity

/// 自定義矩陣變換运授,需要掌握矩陣變換的知識才知道怎么用。參照上面推薦的原理鏈接
CGAffineTransformMake(CGFloat a, CGFloat b, CGFloat c, CGFloat d, CGFloat tx, CGFloat ty)

/// 旋轉視圖乔煞。傳入?yún)?shù)為 角度 * (M_PI / 180)。等同于 CGAffineTransformRotate(self.transform, angle)
CGAffineTransformMakeRotation(CGFloat angle)
CGAffineTransformRotate(CGAffineTransform t, CGFloat angle)

/// 縮放視圖柒室。等同于CGAffineTransformScale(self.transform, sx, sy)
CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)
CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy)

/// 縮放視圖渡贾。等同于CGAffineTransformTranslate(self.transform, tx, ty)
CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty)
CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty)

我把demo左下角文字的變形過程記錄下來。這里推薦mac上面的一款截取動圖的程序licecap雄右,非常簡單好用空骚。博主用它來分解動畫步驟,然后進行重現(xiàn)擂仍。

文字變形過程

不難看出在文字的動畫中做了兩個處理:y軸上的形變縮小囤屹、透明度的漸變過程。首先在項目中新增兩個UILabel逢渔,分別命名為label1肋坚、label2.然后在viewDidAppear中加入這么一段代碼:

- (void)viewDidAppear: (BOOL)animated {
    label1.transform = CGAffineTransformMakeScale(1, 0);
    label1.alpha = 0;
    [UIView animateWithDuration: 3. animations: ^ {
        label1.transform = CGAffineTransformMakeScale(1, 1);
        label2.transform = CGAffineTransformMakeScale(1, 0.1);
        label1.alpha = 1;
        label2.alpha = 0;
    }];
}

這里解釋一下為什么label2為什么在動畫中y軸逐漸縮小為0.1而不是0。如果我們設為0的話肃廓,那么在動畫提交之后智厌,label2會直接保持動畫結束的狀態(tài)(這是出于性能優(yōu)化自動完成的),因此在使用任何縮小的形變時盲赊,你可以將縮小值設置的很小铣鹏,只要不是0。

運行你的代碼哀蘑,文字的形變過程你已經做出來了诚卸,但是demo中的動畫不僅僅是形變葵第,還包括位移的過程。很顯然合溺,我們可以通過改變center的位置來實現(xiàn)這個效果卒密,但這顯然不是我們今天想要的結果,實現(xiàn)新的動畫方式來實現(xiàn)更有意義辫愉。

動畫開始時形變出現(xiàn)的label高度為0栅受,然后逐漸的的變高變?yōu)?code>height,而label從頭到尾基于頂部的位置不發(fā)生改變恭朗。因此動畫開始前這個label在y軸上的位置是0屏镊,在完成顯示之后的y軸中心點為height / 2(基于label自身的坐標系而言),那么動畫的代碼就可以寫成這樣:

- (void)viewDidAppear: (BOOL)animated {
    ///  初始化動畫開始前l(fā)abel的位置
    CGFloat offset = label1.frame.size.height * 0.5;
    
    label1.transform = CGAffineTransformConcat(
      CGAffineTransformMakeScale(0, 0),
      CGAffineTransformMakeTranslation(0, -offset)
    );
    label1.alpha = 0;
    [UIView animateWithDuration: 3. animations: ^ {
        ///  還原label1的變換狀態(tài)并形變和偏移label2
        label1.transform = CGAffineTransformIdentifier;
        label2.transform = CGAffineTransformConcat(
          CGAffineTransformMakeScale(0, 0),
          CGAffineTransformMakeTranslation(0, offset)
        );
        label1.alpha = 1;
        label2.alpha = 0;
    }];
}

調整兩個label的位置痰腮,并且設置其中一個透明顯示而芥。然后運行這段代碼,你會發(fā)現(xiàn)文字轉變過程的動畫完成了膀值。

keyframe動畫

將文章開頭的gif圖另存為到本地棍丐,然后使用預覽打開看看,你會發(fā)現(xiàn)預覽中的gif圖變成了很多張的圖片沧踏。實際上歌逢,無論是動畫、電影翘狱、CG等動態(tài)效果秘案,都可以看做是一張張圖片接連渲染實現(xiàn)的,而這些圖片切換的速度足夠快時我們就會當做是動畫潦匈。在此之前我們所講述的平移視圖在UIView動畫提交之后系統(tǒng)會根據(jù)動畫時長計算出視圖移動的所有幀界面阱高,然后逐個渲染。

回到我們demo中的落葉動畫來茬缩,我總共對葉子的center進行過五次修改赤惊,我將落葉平移的線性路徑繪制出來并且標注關鍵的轉折點:

1.png

上面這個平移用UIView動畫代碼要如何實現(xiàn)呢?毫無疑問凰锡,我們需要不斷的嵌套UIView動畫的使用來實現(xiàn)未舟,具體代碼如下:

[self moveLeafWithOffset: (CGPoint){ 15, 80 } completion: ^(BOOL finished) {
    [self moveLeafWithOffset: (CGPoint){ 30, 105 } completion: ^(BOOL finished) {
        [self moveLeafWithOffset: (CGPoint){ 40, 110 } completion: ^(BOOL finished) {
            [self moveLeafWithOffset: (CGPoint){ 90, 80 } completion: ^(BOOL finished) {
                [self moveLeafWithOffset: (CGPoint){ 80, 60 } completion: nil duration: 0.6];
            } duration: 1.2];
        } duration: 1.2];
    } duration: 0.6];
} duration: 0.4];

- (void)moveLeafWithOffset: (CGPoint)offset completion: (void(^)(BOOL finished))completion duration: (NSTimeInterval)duration
{
    [UIView animateWithDuration: duration delay: 0 options: UIViewAnimationOptionCurveLinear animations: ^{
        CGPoint center = _leaf.center;
        center.x += offset.x;
        center.y += offset.y;
        _leaf.center = center;
    } completion: completion];
}

看起來還蠻容易的,上面的代碼只是移動葉子寡夹,在gif圖中我們的葉子還有旋轉处面,因此我們還需要加上這么一段代碼:

[UIView animateWithDuration: 4 animations: ^{
    _leaf.transform = CGAffineTransformMakeRotation(M_PI);
}];

那么ok,運行這段代碼看看菩掏,落葉的移動非常的生硬魂角,我們可以明顯的看到拐角。其次智绸,這段代碼中的duration傳入是沒有任何意義的(傳入一個固定的動畫時長無法體現(xiàn)出在落葉飄下這一過程中的層次步驟)

對于這兩個問題野揪,UIView也提供了另一種動畫方式來幫助我們解決這兩個問題 —— keyframe動畫:
+ (void)animateKeyframesWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewKeyframeAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion
+ (void)addKeyframeWithRelativeStartTime:(double)frameStartTime relativeDuration:(double)frameDuration animations:(void (^)(void))animations
第一個方法是創(chuàng)建一個關鍵幀動畫访忿,第二個方法用于在動畫的代碼塊中插入關鍵幀動畫信息,兩個參數(shù)的意義表示如下:

  • frameStartTime 表示關鍵幀動畫開始的時刻在整個動畫中的百分比
  • frameDuration 表示這個關鍵幀動畫占用整個動畫時長的百分比斯稳。

我做了一張圖片來表示參數(shù)含義:

添加關鍵幀方法參數(shù)說明

對比UIView動畫跟關鍵幀動畫海铆,關鍵幀動畫引入了動畫占比時長的概念,這讓我們能控制每個關鍵幀動畫的占用比例而不是傳入一個無意義的動畫時長 —— 這讓我們的代碼更加難以理解挣惰。當然卧斟,除了動畫占比之外,關鍵幀動畫的options參數(shù)也讓動畫變得更加平滑憎茂,下面是關鍵幀特有的配置參數(shù):

UIViewKeyframeAnimationOptionCalculationModeLinear      // 連續(xù)運算模式珍语,線性
UIViewKeyframeAnimationOptionCalculationModeDiscrete    // 離散運算模式,只顯示關鍵幀
UIViewKeyframeAnimationOptionCalculationModePaced       // 均勻執(zhí)行運算模式竖幔,線性
UIViewKeyframeAnimationOptionCalculationModeCubic       // 平滑運算模式
UIViewKeyframeAnimationOptionCalculationModeCubicPaced  // 平滑均勻運算模式

在demo中我使用的是UIViewKeyframeAnimationOptionCalculationModeCubic板乙,這個參數(shù)使用了貝塞爾曲線讓落葉的下落動畫變得更加平滑。效果可見最開始的gif動畫拳氢,你可以修改demo傳入的不同參數(shù)來查看效果募逞。接下來我們就根據(jù)新的方法把上面的UIView動畫轉換成關鍵幀動畫代碼,具體代碼如下:

[UIView animateKeyframesWithDuration: 4 delay: 0 options: UIViewKeyframeAnimationOptionCalculationModeLinear animations: ^{
    __block CGPoint center = _leaf.center;
    [UIView addKeyframeWithRelativeStartTime: 0 relativeDuration: 0.1 animations: ^{
        _leaf.center = (CGPoint){ center.x + 15, center.y + 80 };
    }];
    [UIView addKeyframeWithRelativeStartTime: 0.1 relativeDuration: 0.15 animations: ^{
        _leaf.center = (CGPoint){ center.x + 45, center.y + 185 };
    }];
    [UIView addKeyframeWithRelativeStartTime: 0.25 relativeDuration: 0.3 animations: ^{
        _leaf.center = (CGPoint){ center.x + 90, center.y + 295 };
    }];
    [UIView addKeyframeWithRelativeStartTime: 0.55 relativeDuration: 0.3 animations: ^{
        _leaf.center = (CGPoint){ center.x + 180, center.y + 375 };
    }];
    [UIView addKeyframeWithRelativeStartTime: 0.85 relativeDuration: 0.15 animations: ^{
        _leaf.center = (CGPoint){ center.x + 260, center.y + 435 };
    }];
    [UIView addKeyframeWithRelativeStartTime: 0 relativeDuration: 1 animations: ^{
        _leaf.transform = CGAffineTransformMakeRotation(M_PI);
    }];
} completion: nil];

可以看到相比UIView的動畫馋评,關鍵幀動畫更加直觀的讓我們明白每一次平移動畫的時間占比放接,代碼也相對的更加簡潔。

尾言

本文作為動畫篇的第二篇博客留特,到了這里UIView的所有動畫教程已經完成透乾,在之后的文章中將進一步講解autolayout動畫和圖層層次的動畫。時值新年磕秤,祝愿各位??年快樂,心想事成捧韵!本文demo地址

上一篇:從UIView動畫說起
下一篇:layout動畫初體驗

轉載請注明地址和原文作者

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末市咆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子再来,更是在濱河造成了極大的恐慌蒙兰,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芒篷,死亡現(xiàn)場離奇詭異搜变,居然都是意外死亡,警方通過查閱死者的電腦和手機针炉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門挠他,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人篡帕,你說我怎么就攤上這事殖侵∶衬兀” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵拢军,是天一觀的道長楞陷。 經常有香客問我,道長茉唉,這世上最難降的妖魔是什么固蛾? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮度陆,結果婚禮上艾凯,老公的妹妹穿的比我還像新娘。我一直安慰自己坚芜,他們只是感情好览芳,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鸿竖,像睡著了一般沧竟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上缚忧,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天悟泵,我揣著相機與錄音,去河邊找鬼闪水。 笑死糕非,一個胖子當著我的面吹牛,可吹牛的內容都是我干的球榆。 我是一名探鬼主播朽肥,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼持钉!你這毒婦竟也來了衡招?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤每强,失蹤者是張志新(化名)和其女友劉穎始腾,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體空执,經...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡浪箭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了辨绊。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奶栖。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出驼抹,到底是詐尸還是另有隱情桑孩,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布框冀,位于F島的核電站流椒,受9級特大地震影響,放射性物質發(fā)生泄漏明也。R本人自食惡果不足惜宣虾,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望温数。 院中可真熱鬧绣硝,春花似錦、人聲如沸撑刺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽够傍。三九已至甫菠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間冕屯,已是汗流浹背寂诱。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留安聘,地道東北人痰洒。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像浴韭,于是被迫代替她去往敵國和親丘喻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內容

  • 序言 追求美好是人的天性念颈,這是猿們無法避免的仓犬。我們總是追求更為酷炫的實現(xiàn),如果足夠仔細舍肠,我們不難發(fā)現(xiàn)一個好的動畫通...
    姚欽閱讀 770評論 0 3
  • 在iOS中隨處都可以看到絢麗的動畫效果,實現(xiàn)這些動畫的過程并不復雜窘面,今天將帶大家一窺iOS動畫全貌翠语。在這里你可以看...
    F麥子閱讀 5,094評論 5 13
  • 在iOS中隨處都可以看到絢麗的動畫效果,實現(xiàn)這些動畫的過程并不復雜财边,今天將帶大家一窺ios動畫全貌肌括。在這里你可以看...
    每天刷兩次牙閱讀 8,465評論 6 30
  • 前言: UIVIew Animation 是 iOS 提供的最基礎的一組用于實現(xiàn) UIView 動畫的類庫。在 U...
    謝謝生活閱讀 3,269評論 0 5
  • iOS核心動畫-Core Animation 概論 目標:1. 學會使用圖層精簡非交互式繪圖;2. 通過核心動畫創(chuàng)...
    ValienZh閱讀 1,882評論 2 9