iOS7推出了新的轉(zhuǎn)場動畫API,以協(xié)議id<UIViewControllerInterativeTransition>尽棕、id<UIViewAnimatedTransitioning>方式開放給開發(fā)者喳挑,不同于代理、類別滔悉,這樣更易于我們自定義動畫伊诵,更加靈活。下面介紹一下自定義轉(zhuǎn)場動畫
要使用的協(xié)議
- UIViewControllerInteractiveTransitioning 交互協(xié)議回官,主要在右滑返回時用到
- UIViewControllerAnimatedTransitioning 動畫協(xié)議曹宴,含有動畫時間及轉(zhuǎn)場上下文兩個必須實現(xiàn)協(xié)議
- UIViewControllerContextTransitioning 動畫協(xié)議里邊的協(xié)議之一,動畫實現(xiàn)的主要部分
- UIPrecentDrivenInteractiveTransition 用在交互協(xié)議歉提,百分比控制當前動畫進度笛坦。
自定義步驟
- 首先要實現(xiàn)navigation的代理,navigation有兩個返回id類型的協(xié)議苔巨,實現(xiàn)這兩個協(xié)議
第一個方法返回一個UIPercentDrivenInterativeTransition類型的對象即可版扩,這個對象默認實現(xiàn)了UIPercentInterativeTransitioning協(xié)議,需要注意的是侄泽,這個返回值主要是用于交互動畫礁芦,也就是右滑返回時需要用到
,這里我在基類baseViewController定義了一個UIPercentDrivenInterativeTransition類型的屬性。
- (nullable id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController )navigationController
interactionControllerForAnimationController:(WTKBaseAnimation) animationControlle{
return animationControlle.interactivePopTransition;
}
第二個方法我自定義了一個遵循UIViewControllerAnimationTransitioning協(xié)議的類WTKBaseAnimation
,
- (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(WTKBaseViewController *)fromVC
toViewController:(UIViewController *)toVC{
if (fromVC.interactivePopTransition)
{
WTKBaseAnimation *animation = [[WTKBaseAnimation alloc]initWithType:operation Duration:0.6 animateType:self.animationType];
animation.interactivePopTransition = fromVC.interactivePopTransition;
return animation; //手勢
}
else
{
WTKBaseAnimation *animation = [[WTKBaseAnimation alloc]initWithType:operation Duration:0.6 animateType:self.animationType];
return animation;//非手勢
};}
第二個方法返回對象自定義如下
也就是需要把navigation的代理方法中的參數(shù)都傳過來柿扣。
在本類中肖方,需要實現(xiàn)轉(zhuǎn)場動畫協(xié)議:
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext{
return self.duration;}
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext{
if (self.transitionType == UINavigationControllerOperationPush)
{
[self push:transitionContext];
}
else if (self.transitionType == UINavigationControllerOperationPop)
{
[self pop:transitionContext];
}}
[self push:transitionContext];
[self pop:transitionContext];
在本類中并沒有真正的實現(xiàn),具體交給子類實現(xiàn)
- (void)push:(id<UIViewControllerContextTransitioning>)transitionContext{}
- (void)pop:(id<UIViewControllerContextTransitioning>)transitionContext{}
下面介紹子類的具體實現(xiàn)未状,
push
|
<pre><code>- (void)push:(id<UIViewControllerContextTransitioning>)transitionContext {
UIViewController * fromVc = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController * toVc = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
NSTimeInterval duration = [self transitionDuration:transitionContext];
CGRect bounds = [[UIScreen mainScreen] bounds];
fromVc.view.hidden = YES;
[[transitionContext containerView] addSubview:toVc.view];
[[toVc.navigationController.view superview] insertSubview:fromVc.snapshot belowSubview:toVc.navigationController.view];
toVc.navigationController.view.transform =
CGAffineTransformMakeTranslation(CGRectGetWidth(bounds), 0);
[UIView animateWithDuration:duration
delay:0
usingSpringWithDamping:1.0
initialSpringVelocity:0
options:UIViewAnimationOptionCurveLinear
animations:^{
fromVc.snapshot.transform = CGAffineTransformMakeTranslation(-CGRectGetWidth(bounds) * 0.3, 0);
toVc.navigationController.view.transform = CGAffineTransformMakeTranslation(0, 0);
}
completion:^(BOOL finished) {
fromVc.view.hidden = NO;
[fromVc.snapshot removeFromSuperview];
[transitionContext completeTransition:YES];
}];
}
</code></pre>
其中fromVC與toVC窥妇、為函數(shù)參數(shù)transitionContext協(xié)議獲得,duration調(diào)用父類方法獲得娩践,最終為navigation的代理方法中返回的時間活翩,也可以自定義。fromVC為原來的ViewController翻伺,toVC為要push的VC
首先將fromVC的view隱藏材泄,使用VC的snapshot
代替,snapshot
為viewController的截圖吨岭,這里使用類別關(guān)聯(lián)屬性實現(xiàn)的拉宗。
[transitionContext containerView]
為容器,存轉(zhuǎn)場需要的view辣辫,
分別將toVC.view及fromVC.snapshot添加到容器中旦事,注意添加順序、view存放的順序急灭。
下面將toVC移動到屏幕右邊姐浮,這里使用的是改變transform,
使用UIView做動畫葬馋,需要注意的是卖鲤,使用usingSpringWithDamping
的動畫,關(guān)于這個動畫不再多說畴嘶。
UIView動畫蛋逾,需要把fromVC移動到左邊(移動多少可自定),toVC移動到右邊窗悯。
動畫完成后区匣,需要把原來隱藏的fromVC.view顯示,添加到容器的view移除蒋院,當前顯示的vc不需要移除亏钩。
** 最后需要調(diào)用轉(zhuǎn)場完成方法** [transitionContext completeTransition:YES];
Pop
|
- (void)pop:(id<UIViewControllerContextTransitioning>)transitionContext {
WTKBaseViewController * fromVc = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController * toVc = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
NSTimeInterval duration = [self transitionDuration:transitionContext];
CGRect bounds = [[UIScreen mainScreen] bounds];
[fromVc.view addSubview:fromVc.snapshot];
fromVc.navigationController.navigationBar.hidden = YES;
fromVc.view.transform = CGAffineTransformIdentity;
toVc.view.hidden = YES;
toVc.snapshot.transform = CGAffineTransformMakeTranslation(-CGRectGetWidth(bounds) * 0.3, 0);
[[transitionContext containerView] addSubview:toVc.view];
[[transitionContext containerView] addSubview:toVc.snapshot];
[[transitionContext containerView] sendSubviewToBack:toVc.snapshot];
if (fromVc.interactivePopTransition)
{
[UIView animateWithDuration:duration
delay:0
options:UIViewAnimationOptionCurveLinear
animations:^{
fromVc.view.transform = CGAffineTransformMakeTranslation(CGRectGetWidth(bounds), 0.0);
toVc.snapshot.transform = CGAffineTransformIdentity;
}
completion:^(BOOL finished) {
toVc.navigationController.navigationBar.hidden = NO;
toVc.view.hidden = NO;
[fromVc.snapshot removeFromSuperview];
[toVc.snapshot removeFromSuperview];
fromVc.snapshot = nil;
if (![transitionContext transitionWasCancelled]) {
toVc.snapshot = nil;
}
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
else
{
[UIView animateWithDuration:duration
delay:0
usingSpringWithDamping:1
initialSpringVelocity:0
options:UIViewAnimationOptionCurveLinear
animations:^{
fromVc.view.transform = CGAffineTransformMakeTranslation(CGRectGetWidth(bounds), 0.0);
toVc.snapshot.transform = CGAffineTransformIdentity;
}
completion:^(BOOL finished) {
toVc.navigationController.navigationBar.hidden = NO;
toVc.view.hidden = NO;
[fromVc.snapshot removeFromSuperview];
[toVc.snapshot removeFromSuperview];
fromVc.snapshot = nil;
if (![transitionContext transitionWasCancelled]) {
toVc.snapshot = nil;
}
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}}
pop方法與push類似,不再多說悦污,需要注意的是** 一定要區(qū)分手勢和非手勢 **铸屉,也就是如果點擊按鈕返回钉蒲,需要使用usingSpringWithDamping
動畫切端,右滑返回不使用這個。
判斷方式fromVc.interactivePopTransition
顷啼,這個為在基類baseViewController里邊自定義的一個UIPercentDrivenInterativeTransition類型的屬性踏枣,也就是navigation代理方法- (nullable id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(WTKBaseAnimation*) animationControlle
的返回值昌屉。
- 在baseViewController中添加手勢:UIPanGestureRecognizer,添加到self.view上面,viewDidLoad中如下:
if (self.navigationController && self != self.navigationController.viewControllers.firstObject)
{
UIPanGestureRecognizer *popRecognizer = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(handlePopRecognizer:)];
[self.view addGestureRecognizer:popRecognizer];
popRecognizer.delegate = self;
}
在手勢方法中創(chuàng)建UIPercentInterativeTransition茵瀑,在拖動過程中间驮,用這個實例變量調(diào)用updateInteractiveTransition
方法,代碼如下
- (void)handlePopRecognizer:(UIPanGestureRecognizer *)recognizer{
CGFloat progress = [recognizer translationInView:self.view].x / CGRectGetWidth(self.view.frame);
progress = MIN(1.0, MAX(0.0, progress));
NSLog(@"progress---%.2f",progress);
if (recognizer.state == UIGestureRecognizerStateBegan)
{
self.interactivePopTransition = [[UIPercentDrivenInteractiveTransition alloc]init];
[self.navigationController popViewControllerAnimated:YES];
}
else if (recognizer.state == UIGestureRecognizerStateChanged)
{
[self.interactivePopTransition updateInteractiveTransition:progress];
}
else if (recognizer.state == UIGestureRecognizerStateEnded || recognizer.state == UIGestureRecognizerStateCancelled)
{
if (progress > 0.25)
{
[self.interactivePopTransition finishInteractiveTransition];
}
else
{
[self.interactivePopTransition cancelInteractiveTransition];
}
self.interactivePopTransition = nil;
}}
上面定義的progress
马昨,為了記錄滑動的百分比竞帽,隨時更新interactivePopTransition
當手勢結(jié)束,根據(jù)progress判斷當前是否可以pop回來,這里是以0.25為標準鸿捧。
代碼連接 git連接