iOS動畫-通過CALayer讓你的愛啪啪(APP)動起來

一、iOS動畫

iOS中實現一個動畫十分簡單,在view層面上通過調用

[UIView animateWithDuration:duration animations:^{
        //執行動畫
 }]

但是它不能控制動畫的暫停和組合,所以就需要用到CoreAnimation了。
iOS中的動畫主要分為:基礎動畫(CABasicAnimation)、關鍵幀動畫(CAKeyFrameAnimation)、動畫組(CAAnimationGroup)、轉場動畫(CATransition),關系圖如下

動畫關系圖
  • CAAnimation:核心動畫的基礎類,不能直接使用,負責動畫運行時間、速度控制,本身是實現了CAMediaTiming協議
  • CAPropertyAnimation:屬性動畫的基類,即通過屬性進行動畫設置,不能直接使用。
  • CAAnimationGroup:動畫組,可以通過動畫組來進行所有動畫行為的統一控制,組中所有動畫可以并發執行。
  • CATransition:轉場動畫,主要通過濾鏡進行動畫效果設置。
  • CABasicAnimation:基礎動畫,通過修改屬性進行動畫參數控制,只有初始狀態和結束狀態。
  • CAKeyFrameAnimation:關鍵幀懂的規劃,通過修改屬性進行動畫,可以有多個狀態控制。

二、簡單動畫的實現(CABasicAnimation)

You can perform simple animations implicitly or explicitly depending on your needs. Implicit animations use the default timing and animation properties to perform an animation, whereas explicit animations require you to configure those properties yourself using an animation object. So implicit animations are perfect for situations where you want to make a change without a lot of code and the default timing works well for you.

apple提供隱式動畫和顯式動畫兩種,隱式動畫使用系統默認的時間和屬性,如果要顯式動畫,需要用戶自己配置屬性。當用戶想要用少量代碼和默認的時間實現簡單的動畫,用隱式就行了。

什么是隱式動畫,顯式動畫?
  • 隱式動畫就是不需要手動調用動畫方法,系統默認提供的動畫效果,時間是1/4秒,root layer沒有隱式動畫。當你改變了layer的屬性,apple會立刻把這個屬性改變,但是會通過呈現樹以動畫的形式展示改變的效果(這也就是為什么apple建議通過presentationLayer的屬性來訪問當前屏幕上的layer信息了),這個動態改變的過程就是隱式動畫。
  • 顯式動畫是開發者自定義的動畫效果,可以設置屬性改變的時間長度,改變的效果(淡入淡出等)。

2.1 單個動畫

實現簡單動畫通常都用CABasicAnimation對象和用戶配置好的動畫參數來實現,通過調用basicAni的addAnimation:forKey:方法將動畫添加到layer里面,這個方法會根據填入的key值來決定讓哪個屬性進行動畫。

圖層動畫的本質就是將圖層內部的內容轉化為位圖經硬件操作形成一種動畫效果,其實圖層本身并沒有任何的變化
也就是說動畫不會從根本上改變layer的屬性,只是展示屬性變化的效果

簡單動畫會在一定時間內通過動畫的形式改變layer的屬性值,比如用動畫效果將透明度opacity從1到0:
以透明度為例實現簡單動畫:

CABasicAnimation *fade = [CABasicAnimation animationWithKeyPath:@"opacity"];
fade.fromValue = [NSNumber numberWithFloat:1.0];
fade.toValue = [NSNumber NumberWithFloat:0.0];
fade.duration = 1.0;
//注意key相當于給動畫進行命名,以后獲得該動畫時可以使用此名稱獲取
[self.layer addAnimation:fade forKey:@"opacity];
//動畫不會真正改變opacity的值,在動畫結束后要手動更改
self.opacity = 0;

還可以通過animation 的delegate方法監測動畫是否執行完畢

CABasicAnimation *fade = [CABasicAnimation animationWithKeyPath:@"opacity"];
fade.fromValue = [NSNumber numberWithFloat:1.0];
fade.toValue = [NSNumber NumberWithFloat:0.0];
fade.duration = 1.0;
fade.delegate = self;
//注意key相當于給動畫進行命名,以后獲得該動畫時可以使用此名稱獲取
[self.layer addAnimation:fade forKey:@"opacity];
//動畫不會真正改變opacity的值,在動畫結束后要手動更改

//代理方法
- (void)animationDidStart:(CAAnimation *)anim{
    NSLog(@"START");
}

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
    //如果不通過動畫事務將隱式動畫關閉,會出現動畫運行完畢后會從起點突變到終點。
    [CATransaction begin];
    [CATransaction setDisableActions:YES];
    self.layer.position = [[anim valueForKey:@"KPOINT"] CGPointValue];
    [CATransaction commit];
}
不更改透明度
更改了透明度

Tip: When creating an explicit animation, it is recommended that you always assign a value to the fromValue property of the animation object. If you do not specify a value for this property, Core animation uses the layer’s current value as the starting value. If you already updated the property to its final value, that might not yield the results you want.

顯式動畫,apple建議你總是給fromValue賦值,如果你不賦值給fromValue,系統會取當前的屬性值作為起始點,如果你更新了最終值,可能會得到意向不到的結果(不是很明白它的意思,我的理解是如果沒更改最終值,這個屬性是不會被動畫改變的,動畫只是視覺上的效果,原因見下)。

Unlike an implicit animation, which updates the layer object’s data value, an explicit animation does not modify the data in the layer tree. Explicit animations only produce the animations. At the end of the animation, Core Animation removes the animation object from the layer and redraws the layer using its current data values.
隱式動畫會直接改變該屬性的值,而顯式動畫只是一個動畫效果,Core Animation會根據屬性的最終值重回layer,所有必須在動畫結束之后手動更改屬性值,除非你不想改變該屬性。

2.2 多個動畫組合

實現移動過程中旋轉的動畫效果:

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    CGPoint p = [[touches anyObject] locationInView:self.view];
    CALayer *layer = self.leftBottomView.layer;
    self.layer = layer;
    //添加動畫
    [self positionLayer:layer position:p];
    [self rotate:layer];
}

-(void)rotate:(CALayer *)layer{
    CABasicAnimation *basicAnimation=[CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    basicAnimation.toValue=[NSNumber numberWithFloat:M_PI_2*3];
    basicAnimation.duration=3.0;
    //添加動畫到圖層,注意key相當于給動畫進行命名,以后獲得該動畫時可以使用此名稱獲取
    [layer addAnimation:basicAnimation forKey:@"KCBasicAnimation_Rotation"];
}
-(void)positionLayer:(CALayer *)layer position:(CGPoint)p{
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
    animation.toValue = [NSValue valueWithCGPoint:p];
    animation.duration = 3;
    animation.delegate = self;
    [animation setValue:[NSValue valueWithCGPoint:p] forKey:@"KPOINT"];
    [layer addAnimation:animation forKey:@"KPOSITION"];
}

- (void)animationDidStart:(CAAnimation *)anim{
    NSLog(@"START");
    [self.layer animationForKey:@"KPOSITION"];
}

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
    [CATransaction begin];
    [CATransaction setDisableActions:YES];
    self.layer.position = [[anim valueForKey:@"KPOINT"] CGPointValue];
    [CATransaction commit];
}
多個動畫結合

三、關鍵幀動畫(CAKeyFramedAnimation)

3.1 關鍵幀動畫簡單實現

CABasicAnimation只能設定初始和最終值,動畫也只能是簡單的從一個值到另一個值的變化。CAKeyFramedAnimation能夠通過改變多個值,讓你們的動畫能夠以線性或者非線性的方式展現。可以把keyFramedAnimation看成是許多個basicAnimation組合而成,每兩個變化值之間的動畫看成一個簡單動畫。

以改變layer的position為例通過設置關鍵幀,做出曲線動畫,它會根據設定的path
![Uploading keyframe_717706.gif . . .](pathCGPathRef類型),通過描繪路徑進行關鍵幀動畫控制:

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    CGPoint p = [[touches anyObject] locationInView:self.view];
    CALayer *layer = self.leftBottomView.layer;
    self.layer = layer;
    //開始動畫
    [self keyFramed:layer];
    //動畫結束之后改變layer位置
    layer.position = CGPointMake(layer.position.x, layer.position.y+200);
}

-(void)keyFramed:(CALayer *)layer{
    CAKeyframeAnimation *keyFramedAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    //創建路徑
    CGMutablePathRef path = CGPathCreateMutable();
    CGFloat x = layer.position.x;
    CGFloat y = layer.position.y;
    //添加貝塞爾曲線路徑
    CGPathMoveToPoint(path, nil, x, y);
    CGPathAddCurveToPoint(path, nil, x+200, y, x+200, y+100, x, y+100);
    CGPathAddCurveToPoint(path, nil, x+200, y+100, x+200, y+200, x, y+200);
    keyFramedAnimation.path = path;
    keyFramedAnimation.duration = 5;
    [layer addAnimation:keyFramedAnimation forKey:@"KEYFRAME"];
}
keyFrameAnimation

3.2 其他屬性解析

  • calculationMode:

The calculationMode property defines the algorithm to use in calculating the animation timing. The value of this property affects how the other timing-related properties are used

calculationMode,動畫計算模式,規定了動畫時間的算法,對與時間有關的動畫屬性起作用。它是一個字符串類型,有如下:

//線性動畫,是計算模式的默認值
kCAAnimationLinear
//離散動畫,每幀之間沒有過渡
kCAAnimationDiscrete
//均勻動畫,會忽略keyTimes
kCAAnimationPaced
//平滑執行
kCAAnimationCubic
//平滑均勻執行
kCAAnimationCubicPaced

各個值的動畫效果示意圖:

calculationMode示意圖
  • keyTimes:

The keyTimes property specifies time markers at which to apply each keyframe value

keyTimes用于設置每幀之間的動畫執行時間,但是只有在calculationModeKCAAnimationLinearKCAAnimationDiscreteKCAAnimationCubic的時候才有效。

改變keyTimes來改變每幀的動畫時間:

CAKeyframeAnimation *keyFramedAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    CGMutablePathRef path = CGPathCreateMutable();
    CGFloat x = layer.position.x;
    CGFloat y = layer.position.y;
    CGPathMoveToPoint(path, nil, x, y);
    CGPathAddCurveToPoint(path, nil, x+200, y, x+200, y+100, x, y+100);
    CGPathAddCurveToPoint(path, nil, x+200, y+100, x+200, y+200, x, y+200);
    keyFramedAnimation.path = path;
    keyFramedAnimation.duration = 5;
    keyFramedAnimation.calculationMode = kCAAnimationCubicPaced;
    keyFramedAnimation.keyTimes = @[@0.0,@0.2,@1.0];
    [layer addAnimation:keyFramedAnimation forKey:@"KEYFRAME"];
keytime

四、動畫組

CABasicAnimation和CAKeyFramedAnimatio一次只能改變一個屬性,顯示開發中要實現的動畫效果一般都是多個動畫一起執行的,比如平移過程中加入旋轉效果等等。通過CAAnimationGroup能讓開發者自由組合多個動畫效果。

動畫組的實現并不復雜,只需要將各個動畫創建好再添加到動畫組內就行了。

來給上面的關鍵幀動畫加上旋轉效果試試:

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    CGPoint p = [[touches anyObject] locationInView:self.view];
    CALayer *layer = self.leftBottomView.layer;

    CAAnimationGroup *group = [CAAnimationGroup animation];
    CAKeyframeAnimation *key = [self createKeyFramed:layer];
    CABasicAnimation *rotate = [self createRotate:layer];
    group.animations = @[key,rotate];
    [layer addAnimation:group forKey:@"GROUP"];

    layer.position = CGPointMake(layer.position.x, layer.position.y+200);
}


-(CAKeyframeAnimation *)createKeyFramed:(CALayer *)layer{
    CGFloat x = layer.position.x;
    CGFloat y = layer.position.y;

    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, nil, x, y);
    CGPathAddCurveToPoint(path, nil, x+200, y, x+200, y+100, x, y+100);
    CGPathAddCurveToPoint(path, nil, x+200, y+100, x+200, y+200, x, y+200);
   
    CAKeyframeAnimation *keyFramedAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    keyFramedAnimation.path = path;
    keyFramedAnimation.duration = 5;
    [layer addAnimation:keyFramedAnimation forKey:@"KEYFRAME"];
    return keyFramedAnimation;
}

-(CABasicAnimation *)createRotate:(CALayer *)layer{
    CABasicAnimation *rotate=[CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    rotate.toValue =[NSNumber numberWithFloat:M_PI_2*3];
    rotate.duration =5.0;
    [layer addAnimation:rotate forKey:@"KCBasicAnimation_Rotation"];
    return rotate;
}
GROUP

官方文檔給的示例代碼:

// Animation 1
CAKeyframeAnimation* widthAnim = [CAKeyframeAnimation animationWithKeyPath:@"borderWidth"];
NSArray* widthValues = [NSArray arrayWithObjects:@1.0, @10.0, @5.0, @30.0, @0.5, @15.0, @2.0, @50.0, @0.0, nil];
widthAnim.values = widthValues;
widthAnim.calculationMode = kCAAnimationPaced;
 
// Animation 2
CAKeyframeAnimation* colorAnim = [CAKeyframeAnimation animationWithKeyPath:@"borderColor"];
NSArray* colorValues = [NSArray arrayWithObjects:(id)[UIColor greenColor].CGColor,
            (id)[UIColor redColor].CGColor, (id)[UIColor blueColor].CGColor,  nil];
colorAnim.values = colorValues;
colorAnim.calculationMode = kCAAnimationPaced;
 
// Animation group
CAAnimationGroup* group = [CAAnimationGroup animation];
group.animations = [NSArray arrayWithObjects:colorAnim, widthAnim, nil];
group.duration = 5.0;
 
[myLayer addAnimation:group forKey:@"BorderChanges"];

五、轉場動畫(CATransition)

轉場動畫會為layer的轉換添加一個視覺效果,最常見的是一個layer的消失和另一個layer的出現。

4196_141022104125_1.jpg

子類型:

4196_141022104212_1.jpg
CATransition* transition = [CATransition animation];
transition.startProgress = 0;
transition.endProgress = 1.0;
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromRight;
transition.duration = 1.0;
 
// Add the transition animation to both layers
[myView1.layer addAnimation:transition forKey:@"transition"];
[myView2.layer addAnimation:transition forKey:@"transition"];
 
// Finally, change the visibility of the layers.
myView1.hidden = YES;
myView2.hidden = NO;

六、暫停和繼續動畫

在iOS動畫時間是動畫十分重要的一部分,Core Animation 通過調用 CAMediaTiming協議的屬性和方法來精確的設定時間信息。

However, the local time of a layer can be modified by its parent layers or by its own timing parameters. For example, changing the layer’s speed property causes the duration of animations on that layer (and its sublayers) to change proportionally

每個動畫都有自己的local time,用來管理動畫時間信息。通常兩個layer的local time是十分接近的,因此開發者不必去關心這方面的事情,但是改變了layer的speed屬性的話就會導致動畫的時間周期改變,從而影響各個layer動畫local time。

beginTime指定了動畫開始之前的的延遲時間。這里的延遲從動畫添加到可見圖層的那一刻開始測量,默認是0(就是說動畫會立刻執行)。

speed是一個時間的倍數,默認1.0,減少它會減慢圖層/動畫的時間,增加它會加快速度。如果2.0的速度,那么對于一個duration為1的動畫,實際上在0.5秒的時候就已經完成了。

timeOffset和beginTime類似,但是和增加beginTime導致的延遲動畫不同,增加timeOffset只是讓動畫快進到某一點,例如,對于一個持續1秒的動畫來說,設置timeOffset為0.5意味著動畫將從一半的地方開始。

和beginTime不同的是,timeOffset并不受speed的影響。所以如果你把speed設為2.0,把timeOffset設置為0.5,那么你的動畫將從動畫最后結束的地方開始,因為1秒的動畫實際上被縮短到了0.5秒。然而即使使用了timeOffset讓動畫從結束的地方開始,它仍然播放了一個完整的時長,這個動畫僅僅是循環了一圈,然后從頭開始播放。

Core Animation的 馬赫時間 ,可以使用CACurrentMediaTime來訪問,它返回了設備自從上次啟動到現在的時間(然而這并沒什么卵用),它的真實作用在于對動畫的時間測量提供了一個相對值。

每個CALayer和CoreAnimation都有自己的本地時間概念,系統提供了兩個方法:

-(CFTimeInterval)convertTime:(CGTimeInterval)t fromLayer:(CALayer *)l;
-(CFTimeInterval)convertTime:(CGTimeInterval)t toLayer:(CALayer *)l;

配合父圖層/動畫圖層關系中的beginTimetimeOff,speed,可以控制動畫的暫停,快退/快進的功能:

用一個小demo實現暫停、繼續動畫功能:

- (void)pauseAnimation:(CALayer *)layer{
    CFTimeInterval pauseTime = [self.layer convertTime:CACurrentMediaTime() fromLayer:nil];
    layer.timeOffset = pauseTime;
    layer.speed = 0;
}

- (void)resumeAnimation:(CALayer *)layer{
    CFTimeInterval pauseTime = [self.layer timeOffset];
    layer.timeOffset = 0;
    layer.beginTime = 0;
    layer.speed = 1;
    CFTimeInterval timeSincePause = [self.layer convertTime:CACurrentMediaTime() fromLayer:nil] - pauseTime;
    layer.beginTime = timeSincePause;
}

- (IBAction)pause:(id)sender {
    self.stop = !self.stop;
    if (self.stop) {
        [self animationResume];
    }else{
        [self animationPause];
    }
}

得到的效果


pause

七、動畫事務(CATransaction)

CATransaction負責管理動畫、動畫組的創建和執行。大部分情況下開發者不需要手動創建trasaction(動畫事務),但如果要更精確的控制動畫的屬性,比如duration,timing function等,就要通過手動創建CATrasaction。

開啟新的動畫事務調用start方法,結束事務調用commit方法。

apple給的簡單示例代碼,通過:

[CATransaction begin];
theLayer.zPosition=200.0;
theLayer.opacity=0.0;
[CATransaction commit];

CATransaction通過block運行開發者在動畫在動畫結束之后執行一些其他操作:

[CATransaction setCompletionBlock:^{
          //執行動畫結束之后的操作
    }];

CATransaction通過setValue:forKey來改變動畫的屬性,key值是使用的是系統提供的字符串類型值,以改變動畫時間為例:

[CATransaction begin];
[CATransaction setValue:@10 forKey:kCATransactionAnimationDuration];
[CATransaction comment];

除了時間外還有:

kCATransactionDisableActions
kCATransactionAnimationTimingFunction
kCATransactionCompletionBlock

CATransaction允許一個事務內嵌套多個事務的操作,但是每個動畫事務的begincommit必須配套使用。

//outer transaction
[CATransaction begin];
CATransaction setValue:@2 forKey:kCATransactionAnimationDuration];
layer.positon = CGPointMake(0,0);

//inner transaction
[CATransaction begin];
[CATranscation setValue:@3 forKey:kCATransactionAnimationDuration];
layer.zPosition = 200;
layer.opacity = 0;
//inner transaction completed
[CATransaction commit];

//outer transaction completed
[CATranscation commit]

閱讀原文

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372

推薦閱讀更多精彩內容

  • 在iOS中隨處都可以看到絢麗的動畫效果,實現這些動畫的過程并不復雜,今天將帶大家一窺ios動畫全貌。在這里你可以看...
    每天刷兩次牙閱讀 8,541評論 6 30
  • 在iOS中隨處都可以看到絢麗的動畫效果,實現這些動畫的過程并不復雜,今天將帶大家一窺iOS動畫全貌。在這里你可以看...
    F麥子閱讀 5,130評論 5 13
  • 前言 本文只要描述了iOS中的Core Animation(核心動畫:隱式動畫、顯示動畫)、貝塞爾曲線、UIVie...
    GitHubPorter閱讀 3,645評論 7 11
  • 在iOS實際開發中常用的動畫無非是以下四種:UIView動畫,核心動畫,幀動畫,自定義轉場動畫。 1.UIView...
    請叫我周小帥閱讀 3,128評論 1 23
  • 轉載:http://www.cnblogs.com/jingdizhiwa/p/5601240.html 1.ge...
    F麥子閱讀 1,561評論 0 1