ViewController Transition

iOS視圖控制器詳解

視圖控制器中的視圖顯示在屏幕上有兩種方式:最主要的方式是內(nèi)嵌在容器控制器中,比如 UINavigationController,UITabBarController, UISplitController;由另外一個(gè)視圖控制器顯示它,這種方式通常被稱為模態(tài)(Modal)顯示;具體方式是在 NavigationController 里 push 或 pop 一個(gè) View Controller,在 TabBarController 中切換到其他 View Controller,以 Modal 方式顯示另外一個(gè) View Controller,這些都是 View Controller Transition。在 storyboard 里,每個(gè) View Controller 是一個(gè) Scene,View Controller Transition 便是從一個(gè) Scene 轉(zhuǎn)換到另外一個(gè) Scene;官網(wǎng)鏈接View Controller Programming Guide for iOS

觸發(fā)轉(zhuǎn)場(chǎng)的方式

目前為止,官方支持以下幾種transition方式

1.在 UINavigationController 中 push 和 pop;

2.在 UITabBarController 中切換 Tab;

3.Modal transition:presentation 和 dismissal ,稱為視圖控制器的模態(tài)顯示和消失,但是它的model類型屬性modalPresentationStyle 只能限定在UIModalPresentationFullScreen 或 UIModalPresentationCustom 這兩種模式;

以上三種transition都需要代理和動(dòng)畫控制器才可以實(shí)現(xiàn)自定義動(dòng)畫,觸發(fā)的方式分為三種

1)代碼里調(diào)用相關(guān)動(dòng)作的方法

2)Segue

3)容器 VC,在 UINavigationBar 和 UITabBar 上的相關(guān) Item 的點(diǎn)擊操作

相關(guān)動(dòng)作方法

UINavigationController 中所有修改其viewControllers棧中 VC 的方法,就可以自定義transition動(dòng)畫:

下面分別是push和pop方法

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated;

- (nullable UIViewController *)popViewControllerAnimated:(BOOL)animated;

- (void)setViewControllers:(NSArray*)viewControllers animated:(BOOL)animate;//這個(gè)方法是對(duì)VC棧的整體更新

- (nullable NSArray<__kindof UIViewController *> *)popToRootViewControllerAnimated:(BOOL)animated

UITabBarController

@property(nullable, nonatomic, assign) __kindof UIViewController *selectedViewController;//傳遞的參數(shù)必須是其下的子VC

@property(nonatomic) NSUInteger selectedIndex;//選中控制器的索引

- (void)setViewControllers:(NSArray<__kindof UIViewController *> * __nullable)viewControllers animated:(BOOL)animated;//和上面的差不多意思

Modal

- (void)presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^ __nullable)(void))completion NS_AVAILABLE_IOS(5_0);//Presentation

- (void)dismissViewControllerAnimated: (BOOL)flag completion: (void (^ __nullable)(void))completion NS_AVAILABLE_IOS(5_0);//dismiss

Segue

這種方式是在storyboard里面設(shè)置的:存在兩種方式(transition發(fā)生前修改轉(zhuǎn)場(chǎng)參數(shù)的最后機(jī)會(huì))

performSegueWithIdentifier:sender:

prepareForSegue:sender

Transition解釋

WWDC 2013 Session 218

transition過(guò)程之中,作為容器的父 VC 維護(hù)著多個(gè)子 VC,但在視圖結(jié)構(gòu)上,只保留一個(gè)子 VC 的視圖,所以轉(zhuǎn)場(chǎng)的本質(zhì)是下一場(chǎng)景(子 VC)的視圖替換當(dāng)前場(chǎng)景(子 VC)的視圖以及相應(yīng)的控制器(子 VC)的替換,表現(xiàn)為當(dāng)前視圖消失和下一視圖出現(xiàn),基于此進(jìn)行動(dòng)畫,動(dòng)畫的方式非常多;

iOS 7 以協(xié)議的方式開(kāi)放了自定義轉(zhuǎn)場(chǎng)的 API,協(xié)議的好處是不再拘泥于具體的某個(gè)類,只要是遵守該協(xié)議的對(duì)象都能參與轉(zhuǎn)場(chǎng),非常靈活。轉(zhuǎn)場(chǎng)協(xié)議由5種協(xié)議組成,在實(shí)際中只需要我們提供其中的兩個(gè)或三個(gè)便能實(shí)現(xiàn)絕大部分的transiton動(dòng)畫:

1.Transition代理(Transition Delegate):

實(shí)現(xiàn)自定義Transition的第一步就是提供代理,使用我們自己提供的代理,而不是系統(tǒng)默認(rèn)的的代理

//UINavigationController 的 delegate 屬性遵守該協(xié)議。

//UITabBarController 的 delegate 屬性遵守該協(xié)議。

//UIViewController 的 transitioningDelegate 屬性遵守該協(xié)議。(iOS7新增的)

Transition發(fā)生時(shí)候,UIKit要求代理提供transition動(dòng)畫的構(gòu)件:動(dòng)畫控制器和交互控制器(可選的)

2.動(dòng)畫控制器(Animation Controller)

負(fù)責(zé)添加視圖與及執(zhí)行動(dòng)畫:遵守<UIViewControllerAnimatedTransitioning>協(xié)議

3.交互控制器(Interaction Controller)

通過(guò)交互手段,來(lái)控制動(dòng)畫,遵守<UIViewControllerInteractiveTransitioning>協(xié)議;

4.Transition 上下文(Transition Context)

提供Transition過(guò)程中需要的數(shù)據(jù);遵守<UIViewControllerContextTransitioning>協(xié)議;

5.Transition 協(xié)調(diào)器(Transition Coordinator)

可以在Transition動(dòng)畫發(fā)生的同時(shí)執(zhí)行其他動(dòng)畫;遵守<UIViewControllerTransitionCoordinator>協(xié)議,在IOS7中新增了方法transitionCoordinator()返回一個(gè)遵守協(xié)議的對(duì)象,并且該方法只在控制器Transition 的過(guò)程中才返回一個(gè)類對(duì)象;否則返回nil

非交互Transition

這個(gè)階段需要做兩件事,提供Transition代理,并由代理提供動(dòng)畫控制器(交互控制器和動(dòng)畫控制器是可選實(shí)現(xiàn)的),沒(méi)有實(shí)現(xiàn)或者返回ni的話則使用默認(rèn)的Transition效果。總的來(lái)說(shuō),動(dòng)畫控制器是表現(xiàn)的核心部分,代理方法也非常簡(jiǎn)單,讓我們先從動(dòng)畫控制器入手;

動(dòng)畫控制器協(xié)議

動(dòng)畫控制器負(fù)責(zé)添加視圖以及執(zhí)行動(dòng)畫,遵守UIViewControllerAnimatedTransitioning協(xié)議,該協(xié)議要求實(shí)現(xiàn)以下方法:

-(void)animateTransition:(id)transitionContext;//執(zhí)行動(dòng)畫的地方,最核心的方法。

-(NSTimeInterval)transitionDuration:(id)transitionContext;//返回動(dòng)畫時(shí)間

-(void)animationEnded:(BOOL)transitionCompleted;////如果實(shí)現(xiàn)了,會(huì)在轉(zhuǎn)場(chǎng)動(dòng)畫結(jié)束后調(diào)用,可以執(zhí)行一些收尾工作。

最重要是第一個(gè)方法,遵守<UIViewControllerContextTransitioning>協(xié)議的transition context對(duì)象,提供需要的重要數(shù)據(jù),參與視圖控制器和transition過(guò)程的狀態(tài)信息

可以通過(guò)transitionContext在-(void)animateTransition:(id)transitionContext 這個(gè)方法里面來(lái)獲取動(dòng)畫控制器需要的重要信息,例如根視圖,根控制器,方法如下:

- (UIView *)containerView; //返回容器視圖,獲取Transition動(dòng)畫發(fā)生的地方;

- (nullable __kindof UIViewController *)viewControllerForKey:(UITransitionContextViewControllerKey)key;//獲取視圖控制器(通過(guò)UITransitionContextFromViewControllerKey,UITransitionContextToViewControllerKey這兩個(gè)key)

- (nullable __kindof UIView *)viewForKey:(UITransitionContextViewKey)key NS_AVAILABLE_IOS(8_0); //8.0之后誕生的方法來(lái)獲取根控制器的視圖

@note:通過(guò)viewForKey獲取的視圖就是viewControllerForKey返回控制器的根視圖,或者nil(為nil的情況只有在Modal的Transition中才會(huì)出現(xiàn),因?yàn)閏ontainner中的view不包含presentingView),獲取view的方法如下

UIView *containView = [transitionContext containerView];

UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];

UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

UIView *fromView = fromVC.view;

UIView *toView = toVC.view;

上面的fromView和toView其實(shí)是和viewForKey中相應(yīng)key值獲取的是一樣的

Transition的本質(zhì)是下一個(gè)Scene替換當(dāng)前的Scene,從當(dāng)前過(guò)渡到下一個(gè);

fromView:即將消失或者被替換的視圖,對(duì)應(yīng)的控制器是FromVC;

toView:即將顯示或者要替換的視圖,對(duì)應(yīng)的控制器是toVC;

導(dǎo)航控制器(UINavigationController)的Transition

demo中采取的方法是單獨(dú)生成一個(gè)代理類,這個(gè)類遵守<UINavigationControllerDelegate>

-(id)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {NSUInteger transitionType = operation;

SlideAnimationController *slide = [[SlideAnimationController alloc]init];

slide.transitionType = transitionType;

return slide;

} ?//該方法返回動(dòng)畫控制器,若返回來(lái)的是nil則使用系統(tǒng)默認(rèn)的效果



@note:改類中實(shí)現(xiàn)代理的方法采用的是storyBoard里面的Object對(duì)象設(shè)置的,如果你使用的是self.navigationController.delegate = [xxxx new],那么在初始化,離開(kāi)該方法之后,delegate將重新變?yōu)閚il,然后就不會(huì)再調(diào)用代理方法,原因是因?yàn)閐elegate是弱引用,如果你不采取在storyBoard里面設(shè)置的話,你可以通過(guò)一個(gè)本地變量來(lái)達(dá)到強(qiáng)引用的效果,但是設(shè)置的時(shí)候應(yīng)該也要小心,viewDidload方法設(shè)置的時(shí)候有坑,有可能控制器self.navigationController = nil,設(shè)置出來(lái)的self.navigationController.delegate 肯定也是nil,所以建議在prepareForSegue:sender: 這里設(shè)置比較好;

Demo地址:NavigationControllerTransition

TabBar導(dǎo)航控制器(UITabBarController)

UITabBarController 的轉(zhuǎn)場(chǎng)代理和 UINavigationController 類似,都是通過(guò)動(dòng)畫控制器提供相應(yīng)的方法完成,demo中的lei遵守<UITabBarControllerDelegate>協(xié)議,但是該協(xié)議里面并沒(méi)有提供滑動(dòng)方向的相關(guān)方法,需要我們根據(jù)相關(guān)屬性來(lái)判斷;

-(id)tabBarController:(UITabBarController *)tabBarController animationControllerForTransitionFromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {CGFloat fromIndex = [tabBarController.viewControllers indexOfObject:fromVC];

CGFloat toIndex = [tabBarController.viewControllers indexOfObject:toVC];

NSUInteger tabChangeDirection = toIndex < fromIndex ? TransitionTypeLeft : TransitionTypeRight;

SlideAnimationController *slideAnimationController = [[SlideAnimationController alloc]init];

slideAnimationController.transitionType = tabChangeDirection;

return slideAnimationController;

}

代理方法設(shè)置過(guò)程類似;

Demo:SLScrollTabController

Modal Transition

Modal Transiton 和上面介紹的兩種是有區(qū)別的,上兩個(gè)例子里面可以通過(guò)containerView 獲取當(dāng)前Transition的容器,并且fromVC和toVC都在容器里面,而在這一點(diǎn)上面Modal是有區(qū)別的,在Modal中presentingVC相當(dāng)于fromVC,presentedVC相當(dāng)于toVC,兩者的視圖結(jié)構(gòu)如下


ContainerVC VS Modal


在Modal Transition里面,我們著重講UIModalPresentationFullScreen模式和UIModalPresentationCustom模式,這兩種在modal transition中的機(jī)制又是不一樣的,UIModalPresentationFullScreen 模式下,Modal Transiton結(jié)束后 fromView 依然主動(dòng)被從視圖結(jié)構(gòu)中移除了,但是UIModalPresentationCustom沒(méi)有移除,就是因?yàn)檫@個(gè)區(qū)別導(dǎo)致處理dismissal的時(shí)候容易出現(xiàn)問(wèn)題;

dismissal Transition 場(chǎng)景

1.FullScreen 模式:presentation 結(jié)束后,presentingView 被主動(dòng)移出視圖結(jié)構(gòu),不過(guò),在 dismissal transition中希望其出現(xiàn)在屏幕上并且在對(duì)其添加動(dòng)畫怎么辦呢?實(shí)際上,你按照容器類 VC 轉(zhuǎn)場(chǎng)里動(dòng)畫控制器里那樣做也沒(méi)有問(wèn)題,就是將其加入 containerView 并添加動(dòng)畫。不用擔(dān)心,結(jié)束后,UIKit 會(huì)自動(dòng)將其恢復(fù)到原來(lái)的位置。

2.Custom 模式:presentation 結(jié)束后,presentingView(fromView) 未被主動(dòng)移出視圖結(jié)構(gòu),在 dismissal 中,注意不要像其他轉(zhuǎn)場(chǎng)中那樣將 presentingView(toView) 加入 containerView 中,否則 dismissal 結(jié)束后本來(lái)可見(jiàn)的 presentingView 將會(huì)隨著 containerView 一起被移除。如果你在 Custom 模式下沒(méi)有注意到這點(diǎn),很容易出現(xiàn)黑屏之類的現(xiàn)象而不知道問(wèn)題所在。

雖然ios8以上的系統(tǒng),可以通過(guò)UIPresentationController類并重寫以下方法并返回true可以解決上述問(wèn)題:

// Indicate whether the view controller's view we are transitioning from will be removed from the window in the end of the presentation transition ?(Default: NO)

- (BOOL)shouldRemovePresentersView

@note UIPresentationController 類的作用并沒(méi)有改變上面所說(shuō)的presentingview和containerView的層次關(guān)系,但是能修復(fù)這個(gè)問(wèn)題,應(yīng)該是返回YES之后,同時(shí)對(duì)兩個(gè)視圖進(jìn)行控制

結(jié)論:盡量不要在Modal Transiton中的custome模式中對(duì)presentingView進(jìn)行動(dòng)畫,并且針對(duì)custom模式下的使用,官文文檔比較詳細(xì)Creating Custom Presenting

Demo:SLCustomModalTransitionDemo

交互式Transition

在非交互Transition的基礎(chǔ)之上將之交互需要兩個(gè)條件

1.由轉(zhuǎn)場(chǎng)代理提供交互控制器,這是一個(gè)遵守協(xié)議的對(duì)象,不過(guò)系統(tǒng)已經(jīng)打包好了現(xiàn)成的類UIPercentDrivenInteractiveTransition供我們使用。我們不需要做任何配置,僅僅在Transition代理的相應(yīng)方法中提供一個(gè)該類實(shí)例便能工作。另外交互控制器必須有動(dòng)畫控制器才能工作。

2.交互控制器需要交互手段的配合,常用的是使用手勢(shì);


如果在轉(zhuǎn)場(chǎng)代理中提供了交互控制器,而轉(zhuǎn)場(chǎng)發(fā)生時(shí)并沒(méi)有方法來(lái)驅(qū)動(dòng)轉(zhuǎn)場(chǎng)進(jìn)程(比如手勢(shì)),轉(zhuǎn)場(chǎng)過(guò)程將一直處于開(kāi)始階段無(wú)法結(jié)束,應(yīng)用界面也會(huì)失去響應(yīng):在 NavigationController 中點(diǎn)擊 NavigationBar 也能實(shí)現(xiàn) pop 返回操作,但此時(shí)沒(méi)有了交互手段的支持,Transition過(guò)程卡;可以通過(guò)一個(gè)變量來(lái)標(biāo)記交互狀態(tài),該變量由交互手勢(shì)來(lái)更新?tīng)顟B(tài)

-(instancetype)init {??

? if (self = [super init]) {? ? ?

?? _interactive = false;? ? ? ? _interactionController = [[UIPercentDrivenInteractiveTransition alloc]init];

? ? }? ??

return self;

}

-(id)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {

? ? NSUInteger transitionType = operation;? ?

?SlideAnimationController *slide = [[SlideAnimationController alloc]init];? ??

slide.transitionType = transitionType;? ?

?return slide;

}-(id)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id)animationController {

return _interactive ? _interactionController : nil;

}

TabBarController的實(shí)現(xiàn)也類似;系統(tǒng)打包好的UIPercentDrivenInteractiveTransition中的控制轉(zhuǎn)場(chǎng)進(jìn)度的方法與轉(zhuǎn)場(chǎng)環(huán)境對(duì)象提供的三個(gè)方法同名,實(shí)際上只是前者調(diào)用了后者的方法而已。系統(tǒng)以一種解耦的方式使得動(dòng)畫控制器,交互控制器,轉(zhuǎn)場(chǎng)環(huán)境對(duì)象互相協(xié)作,我們只需要使用UIPercentDrivenInteractiveTransition的三個(gè)同名方法來(lái)控制進(jìn)度就夠了。如果你要實(shí)現(xiàn)自己的交互控制器,而不是UIPercentDrivenInteractiveTransition的子類,就需要調(diào)用轉(zhuǎn)場(chǎng)環(huán)境的三個(gè)方法來(lái)控制進(jìn)度;

交互手段

在上面NavigationControllerTransition 的demo中的Slide控制器提供動(dòng)畫來(lái)實(shí)現(xiàn)右滑返回的效果,綁定方法如下

-(void)handleEdgePanGesture:(UIScreenEdgePanGestureRecognizer *)gesture {

CGFloat translationX = [gesture translationInView:self.view].x;

CGFloat translationBase = self.view.frame.size.width / 3;

CGFloat translationAbs = translationX > 0 ? translationX : -translationX;

CGFloat percent = translationAbs > translationBase ? 1.0 : translationAbs / translationBase; //根據(jù)移動(dòng)距離計(jì)算交互過(guò)程的進(jìn)度。

switch (gesture.state) {

case UIGestureRecognizerStateBegan:

_navigationDelegate = self.navigationController.delegate;////轉(zhuǎn)場(chǎng)開(kāi)始前獲取代理,一旦轉(zhuǎn)場(chǎng)開(kāi)始,VC 將脫離控制器棧,此后 self.navigationController 返回的是 nil。

_navigationDelegate.interactive = true;//更新交互狀態(tài)

[self.navigationController popViewControllerAnimated:YES];//.如果轉(zhuǎn)場(chǎng)代理提供了交互控制器,它將從這時(shí)候開(kāi)始接管轉(zhuǎn)場(chǎng)過(guò)程。

break;

case UIGestureRecognizerStateChanged:

//更新轉(zhuǎn)場(chǎng)進(jìn)度,進(jìn)度數(shù)值范圍為0.0~1.0。

[_navigationDelegate.interactionController updateInteractiveTransition:percent];//.更新進(jìn)度:

break;

case UIGestureRecognizerStateCancelled:

case UIGestureRecognizerStateEnded:

if (percent > 0.5) {//.結(jié)束轉(zhuǎn)場(chǎng):

//完成轉(zhuǎn)場(chǎng),轉(zhuǎn)場(chǎng)動(dòng)畫從當(dāng)前狀態(tài)繼續(xù)直至結(jié)束。(轉(zhuǎn)場(chǎng)動(dòng)畫從當(dāng)前的狀態(tài)將繼續(xù)進(jìn)行直到動(dòng)畫結(jié)束,轉(zhuǎn)場(chǎng)完成)

[_navigationDelegate.interactionController finishInteractiveTransition];////完成轉(zhuǎn)場(chǎng)。

}else {

//取消轉(zhuǎn)場(chǎng),轉(zhuǎn)場(chǎng)動(dòng)畫從當(dāng)前狀態(tài)返回至轉(zhuǎn)場(chǎng)發(fā)生前的狀態(tài)。(被調(diào)用后,轉(zhuǎn)場(chǎng)動(dòng)畫從當(dāng)前的狀態(tài)回?fù)艿匠跏紶顟B(tài),轉(zhuǎn)場(chǎng)取消。)

[_navigationDelegate.interactionController cancelInteractiveTransition];////或者,取消轉(zhuǎn)場(chǎng)。

}

_navigationDelegate.interactive = false;//無(wú)論轉(zhuǎn)場(chǎng)的結(jié)果如何,恢復(fù)為非交互狀態(tài)。

break;

default:

break;

}

}


@note:眾所周知,app的生命周期是按照一定順序的,但是介入了Transiton的時(shí)候,順序就得不到保證了,本來(lái)正確的生命周期應(yīng)該是如下的:

1.viewWillAppear 2.viewDidAppear 3.viewWillDisappear 4.viewDidDisappear

介入Transition之后,順序變得錯(cuò)綜復(fù)雜,可以參考這個(gè)鏈接查看相關(guān)情況The Inconsistent Order of View Transition Events

總結(jié):綜合前面所講的內(nèi)容,都還沒(méi)有辦法實(shí)現(xiàn)Transition中的任意階段的中斷,并執(zhí)行新的動(dòng)畫;但是通過(guò)下面介紹的Transition Coordinator就可以實(shí)現(xiàn)了這種效果

Transition Coordinator

Transition Coordinator使用的時(shí)間比較少,但是它可以在Transition過(guò)程中的任意階段搜集動(dòng)作并在交互中執(zhí)行;

Modal Transition中UIPresentationController類只能通過(guò)轉(zhuǎn)場(chǎng)協(xié)調(diào)器來(lái)與動(dòng)畫控制器同步,并行執(zhí)行其他動(dòng)畫;

使用這兩個(gè)方法

- (BOOL)animateAlongsideTransition:(void (^ __nullable)(idcontext))animation? ? ? ? ? ? ? ? ? ? ? ? completion:(void (^ __nullable)(idcontext))completion;// This alternative API is needed if the view is not a descendent of the container view AND you require this animation// to be driven by a UIPercentDrivenInteractiveTransition interaction controller.- (BOOL)animateAlongsideTransitionInView:(nullable UIView *)view? ? ? ? ? ? ? ? ? ? ? ? ? ? ? animation:(void (^ __nullable)(idcontext))animation? ? ? ? ? ? ? ? ? ? ? ? ? ? ? completion:(void (^ __nullable)(idcontext))completion;

這里它可以在交互式轉(zhuǎn)場(chǎng)結(jié)束時(shí)執(zhí)行一個(gè)閉包

// When a transition changes from interactive to non-interactive then handler is// invoked. The handler will typically then do something depending on whether or// not the transition isCancelled. Note that only interactive transitions can// be cancelled and all interactive transitions complete as non-interactive// ones. In general, when a transition is cancelled the view controller that was// appearing will receive a viewWillDisappear: call, and the view controller// that was disappearing will receive a viewWillAppear: call.? This handler is// invoked BEFORE the "will" method calls are made.- (void)notifyWhenInteractionEndsUsingBlock: (void (^)(idcontext))handler NS_DEPRECATED_IOS(7_0, 10_0,"Use notifyWhenInteractionChangesUsingBlock");

當(dāng)Transition由交互狀態(tài)轉(zhuǎn)變?yōu)榉墙换顟B(tài)(在手勢(shì)交互過(guò)程中則為手勢(shì)結(jié)束時(shí)),無(wú)論Transition的結(jié)果是完成還是被取消,該方法都會(huì)被調(diào)用;得益于閉包,Transition協(xié)調(diào)器可以在轉(zhuǎn)場(chǎng)過(guò)程中的任意階段搜集動(dòng)作并在交互中止后執(zhí)行。閉包中的參數(shù)是一個(gè)遵守協(xié)議的對(duì)象,該對(duì)象由 UIKit 提供,和前面的Transition環(huán)境對(duì)象作用類似;另外交互狀態(tài)結(jié)束時(shí)并非Transition過(guò)程的終點(diǎn)(此后動(dòng)畫控制器提供的Transition動(dòng)畫根據(jù)交互結(jié)束時(shí)的狀態(tài)繼續(xù)或是返回到初始狀態(tài)),而是由動(dòng)畫控制器來(lái)結(jié)束這一切:

- (void)animationEnded:(BOOL) transitionCompleted;


向非交互階段的平滑過(guò)渡

這部分的功能沒(méi)有實(shí)現(xiàn)過(guò),但是找資料發(fā)現(xiàn)使用UIViewControllerInteractiveTransitioning協(xié)議定義了兩個(gè)屬性可以做到平滑過(guò)渡

completionCurve //交互結(jié)束后剩余動(dòng)畫的速率曲線

completionSpeed //交互結(jié)束后動(dòng)畫的開(kāi)始速率由該參數(shù)與原來(lái)的速率相乘得到,實(shí)際上是個(gè)縮放參數(shù),這里應(yīng)該使用單位變化速率(即你要的速率/距離)。注意:completionSpeed會(huì)影響剩余的動(dòng)畫時(shí)間,而不是之前設(shè)定的轉(zhuǎn)場(chǎng)動(dòng)畫時(shí)間剩下的時(shí)間;當(dāng)completionSpeed很小時(shí)剩余的動(dòng)畫時(shí)間可能會(huì)被拉伸得很長(zhǎng),所以過(guò)濾下較低的速率比較好。如果不設(shè)置兩個(gè)參數(shù),轉(zhuǎn)場(chǎng)動(dòng)畫將以原來(lái)的速率曲線在當(dāng)前進(jìn)度的速率繼續(xù)。不過(guò)從實(shí)際使用效果來(lái)看,往往不到0.5s的動(dòng)畫時(shí)間,基本上看不出什么效果來(lái)。


iOS10全程交互控制

在Transition動(dòng)畫里,非交互Transition與交互Transition之間有著明顯的界限:如果以交互轉(zhuǎn)場(chǎng)開(kāi)始,盡管在交互結(jié)束后會(huì)切換到動(dòng)畫過(guò)程,但之后無(wú)法再次切換到交互過(guò)程,只能等待其結(jié)束;如果以非交互Transition開(kāi)始,在動(dòng)畫結(jié)束前是無(wú)法切換到交互過(guò)程的,只能等待其結(jié)束,但是在2016年的WWDC上面介紹的iOS10打破了這個(gè)局面,相關(guān)鏈接WWDC 2016 Session216:Advances in UIKit Animations And Transitions

讓轉(zhuǎn)場(chǎng)動(dòng)畫在非交互狀態(tài)與交互狀態(tài)之間自由切換很困難,UIViewPropertyAnimator類實(shí)現(xiàn)了需要的所有基礎(chǔ)功能,使得難度降低了許多,Demo中使用一個(gè)UIViewPropertyAnimator對(duì)象,就可以實(shí)現(xiàn)轉(zhuǎn)場(chǎng)動(dòng)畫的全程交互控制,甚至不需交互控制器;下面展示Demo中來(lái)實(shí)現(xiàn)Push和Pop過(guò)程全程交互控制的幾個(gè)重要方法:

// 提供一個(gè) UIViewPropertyAnimator,由它來(lái)執(zhí)行轉(zhuǎn)場(chǎng)動(dòng)畫以及實(shí)現(xiàn)交互控制

[animator pauseAnimation];動(dòng)畫暫停

[animator setReversed:true]; 動(dòng)畫反向

[animator startAnimation];開(kāi)始動(dòng)畫

_fractionComplete = animator.fractionComplete; 更新Transition進(jìn)度

[animator continueAnimationWithTimingParameters:[[UISpringTimingParameters alloc]initWithDampingRatio:0.9 initialVelocity:initialVelocity] durationFactor:0]; 手指離開(kāi)屏幕的速度繼續(xù)執(zhí)行剩下的Transition動(dòng)畫,保障動(dòng)畫協(xié)調(diào)過(guò)渡

Demo:PushAndPop


總結(jié):可能上述描述存在錯(cuò)誤,歡迎指出,感謝大家!

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

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