
概述
在AppStore中的應(yīng)用越來越重視動(dòng)畫效果的使用,一個(gè)良好動(dòng)畫效果可以讓兩個(gè)狀態(tài)之間平滑地過度,也可以利用動(dòng)畫吸引住用戶的眼球,在UIView類中共有三個(gè)類目(Category)用于實(shí)現(xiàn)動(dòng)畫功能,分為UIViewAnimation、UIViewAnimationWithBlocks以及UIViewKeyframeAnimations,他們是Apple對(duì)核心動(dòng)畫(Core Animation)的封裝,可以讓我們不進(jìn)行任何繪畫等復(fù)雜操作的前提下實(shí)現(xiàn)大部分動(dòng)畫需求
UIView動(dòng)畫的用處
UIView類中提供的動(dòng)畫功能,允許我們實(shí)現(xiàn)如下動(dòng)畫需求
- 動(dòng)態(tài)修改"可動(dòng)畫屬性"
- frame(位置及大小)
- bounds(大小)
- center(中心點(diǎn)位置)
- transform(形變)
- alpha(透明度)
- backgroundColor(背景顏色)
- contentStretch(可拉伸區(qū)域,從iOS6開始被廢棄)
- 提供轉(zhuǎn)場動(dòng)畫效果
- 修改已存在視圖的子視圖(也可以修改自身)
- 在視圖層級(jí)中用一個(gè)視圖替換另一個(gè)視圖(Block動(dòng)畫提供)
本文中樣例素材
在介紹UIView動(dòng)畫的過程中,我們會(huì)使用一個(gè)UIImageView圖片為例,對(duì)各動(dòng)畫效果進(jìn)行演示
@property (nonatomic, strong) UIImageView *demoImageView;
- (void)viewDidLoad
{
[super viewDidLoad];
self.demoImageView = [[UIImageView alloc] initWithFrame:CGRectMake(20, 20, [[UIScreen mainScreen] bounds].size.width-40, [[UIScreen mainScreen] bounds].size.height-40)];
self.demoImageView.image = [UIImage imageNamed:@"old"];
[self.view addSubview:self.demoImageView];
}
UIViewAnimation
Apple自iOS2開始提供了若干UIView的類方法來實(shí)現(xiàn)動(dòng)畫功能,Apple建議從iOS4系統(tǒng)開始盡量使用Block方式來實(shí)現(xiàn)動(dòng)畫功能,因此我們以后在開發(fā)中應(yīng)該遵從Apple的建議使用Block方式來實(shí)現(xiàn)動(dòng)畫功能,但是學(xué)習(xí)本部分對(duì)未來學(xué)好Block動(dòng)畫有很好的鋪墊作用,因此本文在這里會(huì)對(duì)系統(tǒng)提供的方法進(jìn)行詳盡地介紹
在iOS4之前,我們需要使用動(dòng)畫塊(begin/commit animation block)來進(jìn)行動(dòng)畫,一個(gè)完整的動(dòng)畫塊由以下四部分組成
- 動(dòng)畫塊開始標(biāo)識(shí)
- 配置動(dòng)畫選擇項(xiàng)
- 修改可動(dòng)畫屬性
- 動(dòng)畫塊結(jié)束標(biāo)識(shí)
動(dòng)畫塊開始標(biāo)識(shí)
標(biāo)識(shí)動(dòng)畫塊的開始,該方法是告訴系統(tǒng)將要執(zhí)行一個(gè)或多個(gè)動(dòng)畫,如果通過setAnimationWillStartSelector:或者setAnimationDidStopSelector:方法設(shè)置了代理回調(diào)方法,便可以在回調(diào)方法中收到該方法中的兩個(gè)參數(shù)animationID(動(dòng)畫標(biāo)志)和context(上下文,一般為nil)
// 格式
+ (void)beginAnimations:(nullable NSString *)animationID context:(nullable void *)context;
// 樣例
[UIView beginAnimations:@"demo" context:nil];
動(dòng)畫塊結(jié)束標(biāo)識(shí)
標(biāo)識(shí)動(dòng)畫塊的結(jié)束,同時(shí)安排動(dòng)畫執(zhí)行
// 格式
+ (void)commitAnimations;
// 樣例
[UIView commitAnimations];
設(shè)置動(dòng)畫代理對(duì)象及代理回調(diào)方法
設(shè)置動(dòng)畫代理對(duì)象,以便在動(dòng)畫開始和結(jié)束時(shí)收到系統(tǒng)回調(diào)
// 格式
+ (void)setAnimationDelegate:(nullable id)delegate;
// 樣例
[UIView setAnimationDelegate:self];
設(shè)置動(dòng)畫將要開始時(shí)調(diào)用代理對(duì)象的方法
// 格式
+ (void)setAnimationWillStartSelector:(nullable SEL)selector;
// 樣例
[UIView setAnimationWillStartSelector:@selector(animationWillStart:context:)];
注1: 當(dāng)動(dòng)畫當(dāng)前不可用時(shí),該方法不會(huì)被調(diào)用
注2: 當(dāng)不設(shè)置該方法時(shí),系統(tǒng)默認(rèn)會(huì)調(diào)用- (void)animationWillStart:(NSString *)animationID context:(void *)context
設(shè)置動(dòng)畫已經(jīng)結(jié)束后調(diào)用代理對(duì)象的方法
// 格式
+ (void)setAnimationDidStopSelector:(nullable SEL)selector;
// 樣例
[UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
注1: 當(dāng)動(dòng)畫當(dāng)前不可用時(shí),該方法依然會(huì)被調(diào)用
注2: 當(dāng)不設(shè)置該方法時(shí),系統(tǒng)默認(rèn)會(huì)調(diào)用- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
設(shè)置動(dòng)畫持續(xù)時(shí)長
設(shè)置動(dòng)畫持續(xù)時(shí)長,默認(rèn)0.2s
// 格式
+ (void)setAnimationDuration:(NSTimeInterval)duration;
// 樣例
[UIView setAnimationDuration:0.2f];
注: 該方法必須在修改"可變化屬性"之前調(diào)用
設(shè)置延時(shí)動(dòng)畫時(shí)長
設(shè)置延時(shí)動(dòng)畫時(shí)長,默認(rèn)0.0s
// 格式
+ (void)setAnimationDelay:(NSTimeInterval)delay;
// 樣例
[UIView setAnimationDelay:0.0f];
注: 該方法必須在修改"可變化屬性"之前調(diào)用
設(shè)置動(dòng)畫開始時(shí)間(該方法設(shè)置后整個(gè)動(dòng)畫即不生效,不明原因)
設(shè)置動(dòng)畫開始時(shí)間,默認(rèn)當(dāng)前時(shí)間
// 格式
+ (void)setAnimationStartDate:(NSDate *)startDate;
// 樣例
[UIView setAnimationStartDate:[NSDate date]];
注1: 該方法必須在修改"可變化屬性"之前調(diào)用
注2: 不要在Block動(dòng)畫中調(diào)用該方法
注3: 該方法設(shè)置后整個(gè)動(dòng)畫即不生效,作者嘗試過如下四種方式,結(jié)果都不生效
[UIView setAnimationStartDate:[NSDate date]];
[UIView setAnimationStartDate:[NSDate dateWithTimeIntervalSinceReferenceDate:CFAbsoluteTimeGetCurrent()]];
[UIView setAnimationStartDate:[NSDate dateWithTimeIntervalSinceNow:3]];
[UIView setAnimationStartDate:[NSDate dateWithTimeIntervalSinceReferenceDate:CFAbsoluteTimeGetCurrent()+3]];
設(shè)置時(shí)間函數(shù)
時(shí)間函數(shù)即動(dòng)畫進(jìn)行速度隨著時(shí)間的變化曲線,系統(tǒng)采用一個(gè)枚舉類型來保存
/**
typedef NS_ENUM(NSInteger, UIViewAnimationCurve)
{
UIViewAnimationCurveEaseInOut // 默認(rèn),淡入淡出,先慢后快再慢
UIViewAnimationCurveEaseIn // 淡入,先慢后快
UIViewAnimationCurveEaseOut // 淡出,先快后慢
UIViewAnimationCurveLinear // 線性
};
*/
// 格式
+ (void)setAnimationCurve:(UIViewAnimationCurve)curve;
// 樣例
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
注: 該方法必須在修改"可變化屬性"之前調(diào)用
設(shè)置動(dòng)畫重復(fù)次數(shù)
設(shè)置動(dòng)畫重復(fù)次數(shù),重復(fù)次數(shù)可以是小數(shù),小數(shù)部分會(huì)做相應(yīng)比例的動(dòng)畫,然后直接變化到動(dòng)畫結(jié)束位置
// 格式
+ (void)setAnimationRepeatCount:(float)repeatCount;
// 樣例
[UIView setAnimationRepeatCount:2.5f];
注: 該方法必須在修改"可變化屬性"之前調(diào)用
設(shè)置是否反向動(dòng)畫
設(shè)置是否反向動(dòng)畫,默認(rèn)為NO,如果設(shè)置為YES,動(dòng)畫向前做完會(huì)做一次反向動(dòng)畫,那么一正一反算作一次repeatCount,動(dòng)畫所用時(shí)長會(huì)加倍
// 格式
+ (void)setAnimationRepeatAutoreverses:(BOOL)repeatAutoreverses;
// 樣例
[UIView setAnimationRepeatAutoreverses:YES];
注1: 該方法必須在修改"可變化屬性"之前調(diào)用
注2: 因?yàn)楫?dāng)動(dòng)畫結(jié)束后一定會(huì)定格在動(dòng)畫塊中設(shè)置的新位置上,為了不在最后產(chǎn)生一次不和諧的跳動(dòng),可以將repeatCount設(shè)置成0.5次即可
設(shè)置是否從當(dāng)前狀態(tài)開始動(dòng)畫
該方法用于處理當(dāng)舊動(dòng)畫正在運(yùn)行時(shí)開始一個(gè)新的動(dòng)畫,是否從舊動(dòng)畫的當(dāng)前狀態(tài)開始新動(dòng)畫,默認(rèn)為NO
- 當(dāng)設(shè)置為NO時(shí),舊動(dòng)畫的結(jié)束狀態(tài)會(huì)被作為新動(dòng)畫的初始狀態(tài),且舊動(dòng)畫會(huì)在新動(dòng)畫開始的時(shí)候立刻結(jié)束動(dòng)畫
- 當(dāng)設(shè)置為YES時(shí),舊動(dòng)畫的當(dāng)前狀態(tài)會(huì)被作為新動(dòng)畫的初始狀態(tài)
// 格式
+ (void)setAnimationBeginsFromCurrentState:(BOOL)fromCurrentState;
// 樣例
[UIView setAnimationBeginsFromCurrentState:NO];
設(shè)置轉(zhuǎn)場效果
設(shè)置轉(zhuǎn)場效果,該方法有如下三個(gè)參數(shù)
- 參數(shù)1: 轉(zhuǎn)場動(dòng)畫方向,系統(tǒng)采用一個(gè)枚舉類型來保存
- 參數(shù)2: 參與轉(zhuǎn)場動(dòng)畫的視圖(轉(zhuǎn)場動(dòng)畫效果發(fā)生在該視圖上)
- 參數(shù)3: 如果設(shè)置為YES,則視圖在開始和結(jié)束時(shí)分別渲染一次,在動(dòng)畫過程中不可以更新視圖(這樣性能好),如果設(shè)置為NO,則視圖每一幀都會(huì)渲染,在動(dòng)畫過程中可以實(shí)時(shí)更新視圖(這樣能力強(qiáng))
/**
typedef NS_ENUM(NSInteger, UIViewAnimationTransition)
{
UIViewAnimationTransitionNone,
UIViewAnimationTransitionFlipFromLeft,
UIViewAnimationTransitionFlipFromRight,
UIViewAnimationTransitionCurlUp,
UIViewAnimationTransitionCurlDown
};
*/
// 格式
+ (void)setAnimationTransition:(UIViewAnimationTransition)transition forView:(UIView *)view cache:(BOOL)cache;
// 樣例
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.view cache:NO];
注: 該方法每個(gè)動(dòng)畫塊中只可調(diào)用一次
設(shè)置是否動(dòng)畫可用
設(shè)置是否動(dòng)畫可用,默認(rèn)為YES,如果設(shè)置為NO,則沒有動(dòng)畫效果,屬性變化立即生效
// 格式
+ (void)setAnimationsEnabled:(BOOL)enabled;
// 樣例
[UIView setAnimationsEnabled:YES];
注: 該設(shè)置只影響在這句代碼之后設(shè)置的屬性變化
返回一個(gè)BOOL值標(biāo)識(shí)是否動(dòng)畫可用
// 格式
+ (BOOL)areAnimationsEnabled;
// 樣例
[UIView areAnimationsEnabled];
Apple在iOS7中新增加了一個(gè)簡單的封裝,該方法會(huì)先檢查動(dòng)畫當(dāng)前是否為可用狀態(tài),然后設(shè)置動(dòng)畫為不可用狀態(tài),執(zhí)行Block中的代碼,最后重新恢復(fù)動(dòng)畫為原來狀態(tài)
// 格式
+ (void)performWithoutAnimation:(void (^)(void))actionsWithoutAnimation;
// 樣例
[UIView performWithoutAnimation:^{
self.demoImageView.alpha = 0.4;
}];
動(dòng)畫塊的嵌套
動(dòng)畫塊的嵌套使用,是指在commit提交前一個(gè)動(dòng)畫之前再次調(diào)用begin方法創(chuàng)建一個(gè)新的動(dòng)畫,該新動(dòng)畫可以根據(jù)需求指定與外層動(dòng)畫不同的"動(dòng)畫持續(xù)時(shí)長""時(shí)間函數(shù)"等動(dòng)畫選擇項(xiàng),當(dāng)調(diào)用commit提交一個(gè)動(dòng)畫時(shí),如果當(dāng)前動(dòng)畫塊是在最外層,該方法會(huì)開始做動(dòng)畫,如果當(dāng)前動(dòng)畫塊嵌套在另一個(gè)動(dòng)畫塊的里面,該方法會(huì)等待外層的動(dòng)畫塊提交,直到最外層動(dòng)畫塊提交之后一起做動(dòng)畫,動(dòng)畫是在單獨(dú)的線程運(yùn)行,這樣多個(gè)動(dòng)畫便可以一個(gè)一個(gè)地執(zhí)行起來
// 以"嵌套動(dòng)畫的外層歷時(shí)5s將視圖透明度修改為0.2,嵌套動(dòng)畫的內(nèi)層不繼承外層動(dòng)畫的持續(xù)時(shí)長,將視圖大小縮小一半,期間重復(fù)2.5次"為例,代碼如下
[UIView beginAnimations:@"parent" context:nil];
[UIView setAnimationDuration:5.0f];
self.demoImageView.alpha = 0.2f;
[UIView beginAnimations:@"demo2" context:nil];
[UIView setAnimationDuration:1.0f];
[UIView setAnimationRepeatCount:2.5f];
[UIView setAnimationRepeatAutoreverses:YES];
self.demoImageView.transform = CGAffineTransformMakeScale(0.5, 0.5);
[UIView commitAnimations];
[UIView commitAnimations];

兩個(gè)小例子
例子一
// 以"將視圖大小縮小一半,并做反向動(dòng)畫,最后平滑地停留在預(yù)定位置"為例,代碼如下
[UIView beginAnimations:@"demo" context:nil];
[UIView setAnimationDuration:1.0f];
[UIView setAnimationRepeatCount:2.5];
[UIView setAnimationRepeatAutoreverses:YES];
self.demoImageView.transform = CGAffineTransformMakeScale(0.5, 0.5);
[UIView commitAnimations];

例子二
// 以"從左側(cè)翻轉(zhuǎn)轉(zhuǎn)場動(dòng)畫,且將視圖大小縮小一半(動(dòng)畫效果發(fā)生在該視圖上)"為例,代碼如下
[UIView beginAnimations:@"demo" context:nil];
[UIView setAnimationDuration:1.0f];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.demoImageView cache:YES];
self.demoImageView.transform = CGAffineTransformMakeScale(0.5, 0.5);
[UIView commitAnimations];

UIViewAnimationWithBlocks(Block動(dòng)畫)
Apple從iOS4開始加入了Block功能,UIView的普通動(dòng)畫方法全面增加了對(duì)Block的支持,自此我們便可以非常方便地使用動(dòng)畫功能,推薦使用
普通屬性動(dòng)畫
/**
* 屬性動(dòng)畫
*
* @param duration 動(dòng)畫持續(xù)時(shí)長
* @param delay 延時(shí)動(dòng)畫時(shí)長
* @param options 動(dòng)畫選擇項(xiàng)
* @param animations 動(dòng)畫Block
* @param completion 動(dòng)畫執(zhí)行完執(zhí)行的Block
*
* @return 無返回值
*/
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion;
// 以"將視圖大小縮小一半,動(dòng)畫結(jié)束后恢復(fù)"為例,代碼如下
[UIView animateWithDuration:3.0f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut animations:^{
self.demoImageView.transform = CGAffineTransformMakeScale(0.5, 0.5);
} completion:^(BOOL finished) {
if (finished)
{
self.demoView.transform = CGAffineTransformIdentity;
}
}];
上述Block動(dòng)畫共有5個(gè)參數(shù),使用起來需要設(shè)置的內(nèi)容比較多,系統(tǒng)為我們提供了兩個(gè)相對(duì)簡單的Block動(dòng)畫以用于簡單動(dòng)畫
// delay = 0.0,options = 0的屬性動(dòng)畫
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion;
// delay = 0.0,options = 0,completion = NULL的屬性動(dòng)畫
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations;

彈性屬性動(dòng)畫
Apple從iOS7開始在系統(tǒng)中大量使用SpringAnimation,該動(dòng)畫由于前期速度很快,可以給用戶一種干凈利落的感覺,所以推薦大家使用以與系統(tǒng)動(dòng)畫接軌
/**
* SpringAnimation(彈性動(dòng)畫)
*
* @param duration 動(dòng)畫持續(xù)時(shí)長
* @param delay 延時(shí)動(dòng)畫時(shí)長
* @param dampingRatio 當(dāng)dampingRatio設(shè)置為1時(shí),動(dòng)畫會(huì)毫無震蕩感地平穩(wěn)地運(yùn)動(dòng)到最終狀態(tài),如果dampingRatio的值少于1,值越小在最終狀態(tài)上震蕩效果會(huì)越明顯
* @param velocity 初始速度,數(shù)值越大一開始移動(dòng)速度越快(注: 初始速度取值較高而時(shí)間取值較短時(shí),也會(huì)產(chǎn)生震蕩效果)
* @param options 動(dòng)畫選擇項(xiàng)
* @param animations 動(dòng)畫Block
* @param completion 動(dòng)畫執(zhí)行完執(zhí)行的Block
*
* @return 無返回值
*/
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion;
// 以"將視圖大小縮小一半,動(dòng)畫結(jié)束后恢復(fù)"為例,代碼如下
[UIView animateWithDuration:3.0f delay:0.0f usingSpringWithDamping:0.7f initialSpringVelocity:5.0f options:UIViewAnimationOptionCurveEaseInOut animations:^{
self.demoImageView.transform = CGAffineTransformMakeScale(0.5, 0.5);
} completion:^(BOOL finished) {
if (finished)
{
self.demoView.transform = CGAffineTransformIdentity;
}
}];

執(zhí)行系統(tǒng)動(dòng)畫
Apple從iOS7開始加入了一個(gè)方法,允許我們執(zhí)行系統(tǒng)動(dòng)畫,且可以并行地執(zhí)行附加動(dòng)畫
/**
* 在一個(gè)或多個(gè)視圖上執(zhí)行系統(tǒng)動(dòng)畫
*
* @param animation 系統(tǒng)動(dòng)畫,暫時(shí)只有UISystemAnimationDelete一個(gè)
* @param views 執(zhí)行動(dòng)畫的views數(shù)組
* @param options 動(dòng)畫選擇項(xiàng)
* @param parallelAnimations 并行附加動(dòng)畫Block
* @param completion 動(dòng)畫執(zhí)行完執(zhí)行的Block
*
* @return 無返回值
*/
+ (void)performSystemAnimation:(UISystemAnimation)animation onViews:(NSArray<__kindof UIView *> *)views options:(UIViewAnimationOptions)options animations:(void (^ __nullable)(void))parallelAnimations completion:(void (^ __nullable)(BOOL finished))completion;
// 以"執(zhí)行系統(tǒng)刪除動(dòng)畫"為例,代碼如下
[UIView performSystemAnimation:UISystemAnimationDelete onViews:@[self.demoImageView] options:UIViewAnimationOptionCurveEaseInOut animations:^{
// do something....
} completion:^(BOOL finished) {
if (finished)
{
// do something....
}
}];
注: 附加動(dòng)畫不要修改正在被系統(tǒng)動(dòng)畫修改的屬性

轉(zhuǎn)場動(dòng)畫
Apple為我們提供了兩個(gè)方法用于進(jìn)行轉(zhuǎn)場動(dòng)畫,為我們解決兩個(gè)動(dòng)畫需求
- 修改已存在視圖的子視圖(也可以修改自身)
/**
* TransitionAnimation(轉(zhuǎn)場動(dòng)畫)
*
* @param view 參與轉(zhuǎn)場動(dòng)畫的視圖(轉(zhuǎn)場動(dòng)畫效果發(fā)生在該視圖上)
* @param duration 動(dòng)畫持續(xù)時(shí)長
* @param options 動(dòng)畫選擇項(xiàng)
* @param animations 動(dòng)畫Block
* @param completion 動(dòng)畫執(zhí)行完執(zhí)行的Block
*
* @return 無返回值
*/
+ (void)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^ __nullable)(void))animations completion:(void (^ __nullable)(BOOL finished))completion;
// 以"父視圖從左側(cè)翻轉(zhuǎn)轉(zhuǎn)場動(dòng)畫,且將子視圖大小縮小一半,動(dòng)畫結(jié)束后恢復(fù)"為例,代碼如下
[UIView transitionWithView:self.view duration:3.0f options:UIViewAnimationOptionTransitionFlipFromLeft | UIViewAnimationOptionAllowAnimatedContent animations:^{
self.demoImageView.transform = CGAffineTransformMakeScale(0.5, 0.5);
} completion:^(BOOL finished) {
if (finished)
{
self.demoImageView.transform = CGAffineTransformIdentity;
}
}];
注: 該轉(zhuǎn)場動(dòng)畫作用于view上

- 在視圖層級(jí)中用一個(gè)視圖替換另一個(gè)視圖
/**
* TransitionAnimation(轉(zhuǎn)場動(dòng)畫)
*
* @param fromView 參與轉(zhuǎn)場動(dòng)畫的視圖(被移除視圖)
* @param toView 參與轉(zhuǎn)場動(dòng)畫的視圖(被添加視圖)
* @param duration 動(dòng)畫持續(xù)時(shí)長
* @param options 動(dòng)畫選擇項(xiàng)
* @param completion 動(dòng)畫執(zhí)行完執(zhí)行的Block
*
* @return 無返回值
*/
+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^ __nullable)(BOOL finished))completion;
// 以"從左側(cè)翻轉(zhuǎn)轉(zhuǎn)場動(dòng)畫,將原視圖移除,添加新視圖進(jìn)來"為例,代碼如下
UIImageView *toImageView = [[UIImageView alloc] initWithFrame:CGRectMake(20, 20, [[UIScreen mainScreen] bounds].size.width-40, [[UIScreen mainScreen] bounds].size.height-40)];
toImageView.image = [UIImage imageNamed:@"new"];
[UIView transitionFromView:self.demoImageView toView:toImageView duration:3.0f options:UIViewAnimationOptionTransitionFlipFromLeft completion:^(BOOL finished) {
if (finished)
{
NSLog(@"TransitionAnimation finished");
}
}];
注: 該轉(zhuǎn)場動(dòng)畫作用于fromView的父視圖上,相當(dāng)于將toView添加到fromView的父視圖上,并將fromView從父視圖上移除

Block動(dòng)畫的嵌套
Block動(dòng)畫的嵌套使用,是指在Block動(dòng)畫的animations中再創(chuàng)建一個(gè)新的Block動(dòng)畫,該新動(dòng)畫可以根據(jù)需求指定與外層動(dòng)畫不同的"動(dòng)畫選擇項(xiàng)",新動(dòng)畫會(huì)繼承外層動(dòng)畫的"動(dòng)畫持續(xù)時(shí)長"和"時(shí)間函數(shù)",但是一些"動(dòng)畫選擇項(xiàng)"不會(huì)繼承(在動(dòng)畫選擇項(xiàng)中提供了兩個(gè)用于嵌套動(dòng)畫的選擇項(xiàng),UIViewAnimationOptionOverrideInheritedDuration代表忽略嵌套動(dòng)畫中外層動(dòng)畫的時(shí)長設(shè)置,UIViewAnimationOptionOverrideInheritedCurve代表忽略嵌套動(dòng)畫中外層動(dòng)畫的時(shí)間函數(shù)設(shè)置)
// 以"嵌套動(dòng)畫的外層歷時(shí)5s將視圖透明度修改為0.2,嵌套動(dòng)畫的內(nèi)層不繼承外層動(dòng)畫的持續(xù)時(shí)長,將視圖大小縮小一半,期間重復(fù)2.5次"為例,代碼如下
[UIView animateWithDuration:5.0f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut animations:^{
self.demoImageView.alpha = 0.2f;
[UIView animateWithDuration:1.0f delay:0.0f options:UIViewAnimationOptionOverrideInheritedDuration | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse animations:^{
[UIView setAnimationRepeatCount:2.5];
self.demoImageView.transform = CGAffineTransformMakeScale(0.5, 0.5);
} completion:nil];
} completion:nil];
注: 如果不使用UIViewAnimationOptionOverrideInheritedDuration,嵌套動(dòng)畫的內(nèi)層會(huì)直接繼承外層的持續(xù)時(shí)長

Block動(dòng)畫選擇項(xiàng)
typedef NS_OPTIONS(NSUInteger, UIViewAnimationOptions)
{
// 通用設(shè)置(可同時(shí)設(shè)置多個(gè))
UIViewAnimationOptionLayoutSubviews = 1 << 0, // 動(dòng)畫過程中,子視圖隨著一起動(dòng)畫
UIViewAnimationOptionAllowUserInteraction = 1 << 1, // 動(dòng)畫過程中,允許用戶交互
UIViewAnimationOptionBeginFromCurrentState = 1 << 2, // 是否從舊動(dòng)畫的當(dāng)前狀態(tài)開始新動(dòng)畫,參考UIView的+(void)setAnimationBeginsFromCurrentState:(BOOL)fromCurrentState;方法
UIViewAnimationOptionRepeat = 1 << 3, // 無限次數(shù)重復(fù)動(dòng)畫
UIViewAnimationOptionAutoreverse = 1 << 4, // 是否開啟反向動(dòng)畫,參考UIView的+(void)setAnimationRepeatAutoreverses:方法(BOOL)repeatAutoreverses;方法
UIViewAnimationOptionOverrideInheritedDuration = 1 << 5, // 忽略嵌套動(dòng)畫中外層動(dòng)畫的時(shí)長設(shè)置(只適用于嵌套動(dòng)畫)
UIViewAnimationOptionOverrideInheritedCurve = 1 << 6, // 忽略嵌套動(dòng)畫中外層動(dòng)畫的時(shí)間函數(shù)設(shè)置(只適用于嵌套動(dòng)畫)
UIViewAnimationOptionAllowAnimatedContent = 1 << 7, // 視圖每一幀都會(huì)渲染,在動(dòng)畫過程中可以實(shí)時(shí)更新視圖(只適用于轉(zhuǎn)場動(dòng)畫)
UIViewAnimationOptionShowHideTransitionViews = 1 << 8, // 視圖切換時(shí)直接隱藏舊視圖并顯示新視圖,而不是將舊視圖從父視圖移除并添加新視圖(只適用于轉(zhuǎn)場動(dòng)畫)
UIViewAnimationOptionOverrideInheritedOptions = 1 << 9, // 不繼承動(dòng)畫選擇項(xiàng)(只適用于嵌套動(dòng)畫)
// 時(shí)間函數(shù)控制(只可同時(shí)設(shè)置一項(xiàng)),參考UIView的+(void)setAnimationCurve:(UIViewAnimationCurve)curve;方法
UIViewAnimationOptionCurveEaseInOut = 0 << 16, // 默認(rèn),淡入淡出,先慢后快再慢
UIViewAnimationOptionCurveEaseIn = 1 << 16, // 淡入,先慢后快
UIViewAnimationOptionCurveEaseOut = 2 << 16, // 淡出,先快后慢
UIViewAnimationOptionCurveLinear = 3 << 16, // 線性
// 轉(zhuǎn)場動(dòng)畫方向(只適用于轉(zhuǎn)場動(dòng)畫,只可同時(shí)設(shè)置一項(xiàng)),參考UIView的+(void)setAnimationTransition:(UIViewAnimationTransition)transition forView:(UIView *)view cache:(BOOL)cache;方法
UIViewAnimationOptionTransitionNone = 0 << 20, // 默認(rèn),無效果
UIViewAnimationOptionTransitionFlipFromLeft = 1 << 20, // 從左側(cè)翻轉(zhuǎn)
UIViewAnimationOptionTransitionFlipFromRight = 2 << 20, // 從右側(cè)翻轉(zhuǎn)
UIViewAnimationOptionTransitionCurlUp = 3 << 20, // 向后翻頁
UIViewAnimationOptionTransitionCurlDown = 4 << 20, // 向前翻頁
UIViewAnimationOptionTransitionCrossDissolve = 5 << 20, // 舊視圖溶解顯示新視圖
UIViewAnimationOptionTransitionFlipFromTop = 6 << 20, // 從上方翻轉(zhuǎn)
UIViewAnimationOptionTransitionFlipFromBottom = 7 << 20, // 從下方翻轉(zhuǎn)
}
UIViewKeyframeAnimations(關(guān)鍵幀動(dòng)畫)
Apple從iOS7開始加入了關(guān)鍵幀動(dòng)畫,可以通過提供一個(gè)動(dòng)畫在多個(gè)關(guān)鍵幀處的屬性實(shí)現(xiàn)一系列動(dòng)畫效果,兩個(gè)關(guān)鍵幀中間會(huì)有系統(tǒng)自動(dòng)加入補(bǔ)間動(dòng)畫
/**
* KeyframeAnimation(關(guān)鍵幀動(dòng)畫)
*
* @param duration 動(dòng)畫持續(xù)時(shí)長
* @param delay 延時(shí)動(dòng)畫時(shí)長
* @param options 關(guān)鍵幀動(dòng)畫選擇項(xiàng)
* @param animations 動(dòng)畫Block
* @param completion 動(dòng)畫執(zhí)行完執(zhí)行的Block
*
* @return 無返回值
*/
+ (void)animateKeyframesWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewKeyframeAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion;
/**
* 添加關(guān)鍵幀
*
* @param frameStartTime 幀動(dòng)畫相對(duì)開始時(shí)間,取值為0~1
* @param frameDuration 幀動(dòng)畫相對(duì)持續(xù)時(shí)長,取值為0~1
* @param animations 幀動(dòng)畫Block
*
* @return 無返回值
*/
+ (void)addKeyframeWithRelativeStartTime:(double)frameStartTime relativeDuration:(double)frameDuration animations:(void (^)(void))animations;
// 以"將視圖大小縮小一半,再順時(shí)針旋轉(zhuǎn)90度,再放大2倍,再逆時(shí)針旋轉(zhuǎn)90度恢復(fù)原樣"為例,代碼如下
[UIView animateKeyframesWithDuration:3.0f delay:0.0f options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{
[UIView addKeyframeWithRelativeStartTime:0.0f relativeDuration:0.25f animations:^{
self.demoImageView.transform = CGAffineTransformScale(self.demoImageView.transform, 0.5, 0.5);
}];
[UIView addKeyframeWithRelativeStartTime:0.25f relativeDuration:0.25f animations:^{
self.demoImageView.transform = CGAffineTransformRotate(self.demoImageView.transform, M_PI * 0.5);
}];
[UIView addKeyframeWithRelativeStartTime:0.5f relativeDuration:0.25f animations:^{
self.demoImageView.transform = CGAffineTransformScale(self.demoImageView.transform, 2, 2);
}];
[UIView addKeyframeWithRelativeStartTime:0.75f relativeDuration:0.25f animations:^{
self.demoImageView.transform = CGAffineTransformRotate(self.demoImageView.transform, -M_PI * 0.5);
}];
} completion:^(BOOL finished) {
if (finished)
{
NSLog(@"TransitionAnimation finished");
}
else
{
NSLog(@"在動(dòng)畫執(zhí)行過程中再次被觸發(fā)動(dòng)畫,會(huì)調(diào)用這里后重新開始動(dòng)畫");
}
}];
注: 關(guān)鍵幀動(dòng)畫有兩種形式,UIView只支持屬性關(guān)鍵幀動(dòng)畫,暫不支持路徑關(guān)鍵幀動(dòng)畫

關(guān)鍵幀動(dòng)畫選擇項(xiàng)
typedef NS_OPTIONS(NSUInteger, UIViewKeyframeAnimationOptions)
{
// 通用設(shè)置(可同時(shí)設(shè)置多個(gè))
UIViewKeyframeAnimationOptionLayoutSubviews = UIViewAnimationOptionLayoutSubviews, // 動(dòng)畫過程中,子視圖隨著一起動(dòng)畫
UIViewKeyframeAnimationOptionAllowUserInteraction = UIViewAnimationOptionAllowUserInteraction, // 動(dòng)畫過程中,允許用戶交互
UIViewKeyframeAnimationOptionBeginFromCurrentState = UIViewAnimationOptionBeginFromCurrentState, // 是否從舊動(dòng)畫的當(dāng)前狀態(tài)開始新動(dòng)畫
UIViewKeyframeAnimationOptionRepeat = UIViewAnimationOptionRepeat, // 無限次數(shù)重復(fù)動(dòng)畫
UIViewKeyframeAnimationOptionAutoreverse = UIViewAnimationOptionAutoreverse, // 是否開啟反向動(dòng)畫
UIViewKeyframeAnimationOptionOverrideInheritedDuration = UIViewAnimationOptionOverrideInheritedDuration, // 忽略嵌套動(dòng)畫中外層動(dòng)畫的時(shí)長設(shè)置(只適用于嵌套動(dòng)畫)
UIViewKeyframeAnimationOptionOverrideInheritedOptions = UIViewAnimationOptionOverrideInheritedOptions, // 忽略嵌套動(dòng)畫中外層動(dòng)畫的時(shí)間函數(shù)設(shè)置(只適用于嵌套動(dòng)畫)
// 設(shè)置兩個(gè)關(guān)鍵幀之間的動(dòng)畫模式(可理解為補(bǔ)間動(dòng)畫模式)(只可同時(shí)設(shè)置一項(xiàng))
UIViewKeyframeAnimationOptionCalculationModeLinear = 0 << 10, // 默認(rèn),線性運(yùn)算模式(勻速補(bǔ)間動(dòng)畫)
UIViewKeyframeAnimationOptionCalculationModeDiscrete = 1 << 10, // 離散運(yùn)算模式(無補(bǔ)間動(dòng)畫)
UIViewKeyframeAnimationOptionCalculationModePaced = 2 << 10, // 均勻運(yùn)算模式
UIViewKeyframeAnimationOptionCalculationModeCubic = 3 << 10, // 平滑運(yùn)算模式
UIViewKeyframeAnimationOptionCalculationModeCubicPaced = 4 << 10 // 平滑均勻運(yùn)算模式
}