iOS自定義轉場動畫(push、pop動畫)
iOS7推出了新的轉場動畫API,以協議id<UIViewControllerInterativeTransition>、id<UIViewAnimatedTransitioning>方式開放給開發者,不同於代理、類別,這樣更易於我們自定義動畫,更加靈活。下面介紹一下自定義轉場動畫
要使用的協議
- UIViewControllerInteractiveTransitioning 互動協議,主要在右滑返回時用到
- UIViewControllerAnimatedTransitioning 動畫協議,含有動畫時間及轉場上下文兩個必須實現協議
- UIViewControllerContextTransitioning 動畫協議裡邊的協議之一,動畫實現的主要部分
- UIPrecentDrivenInteractiveTransition 用在互動協議,百分比控制當前動畫進度。
自定義步驟
- 首先要實現navigation的代理,navigation有兩個返回id型別的協議,實現這兩個協議
第一個方法返回一個UIPercentDrivenInterativeTransition型別的物件即可,這個物件預設實現了UIPercentInterativeTransitioning協議,需要注意的是,這個返回值主要是用於互動動畫,也就是右滑返回時需要用到
,這裡我在基類baseViewController定義了一個UIPercentDrivenInterativeTransition型別的屬性。
- (nullable id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController )navigationController
interactionControllerForAnimationController:(WTKBaseAnimation) animationControlle{
return animationControlle.interactivePopTransition;
}
第二個方法我自定義了一個遵循UIViewControllerAnimationTransitioning協議的類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的代理方法中的引數都傳過來。
在本類中,需要實現轉場動畫協議:
- (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];
在本類中並沒有真正的實現,具體交給子類實現
- (void)push:(id<UIViewControllerContextTransitioning>)transitionContext{}
- (void)pop:(id<UIViewControllerContextTransitioning>)transitionContext{}
下面介紹子類的具體實現,
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、為函式引數transitionContext協議獲得,duration呼叫父類方法獲得,最終為navigation的代理方法中返回的時間,也可以自定義。fromVC為原來的ViewController,toVC為要push的VC
首先將fromVC的view隱藏,使用VC的snapshot
代替,snapshot
為viewController的截圖,這裡使用類別關聯屬性實現的。[transitionContext containerView]
為容器,存轉場需要的view,
分別將toVC.view及fromVC.snapshot新增到容器中,注意新增順序、view存放的順序。
下面將toVC移動到螢幕右邊,這裡使用的是改變transform,
使用UIView做動畫,需要注意的是,使用usingSpringWithDamping
的動畫,關於這個動畫不再多說。
UIView動畫,需要把fromVC移動到左邊(移動多少可自定),toVC移動到右邊。
動畫完成後,需要把原來隱藏的fromVC.view顯示,新增到容器的view移除,當前顯示的vc不需要移除。
** 最後需要呼叫轉場完成方法** [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類似,不再多說,需要注意的是** 一定要區分手勢和非手勢 **,也就是如果點選按鈕返回,需要使用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;
}
在手勢方法中建立UIPercentInterativeTransition,在拖動過程中,用這個例項變數呼叫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
當手勢結束,根據progress判斷當前是否可以pop回來,這裡是以0.25為標準。
程式碼連線 git連線
相關文章
- iOS ~ ViewController的Push,Pop和Present,Dismiss轉場動畫iOSViewController動畫
- 自定義Push/Pop和Present/Dismiss轉場
- 一行程式碼實現自定義轉場動畫--iOS自定義轉場動畫集行程動畫iOS
- TransitionAnimation自定義轉場動畫NaN動畫
- 自定義轉場動畫(二)動畫
- 自定義轉場動畫(一)動畫
- iOS自定義轉場動畫實戰講解iOS動畫
- iOS CAAnimation之CATransition (自定義轉場動畫工具類)iOS動畫
- iOS 關於viewController自定義的轉場動畫iOSViewController動畫
- iOS自定義UIView動畫效果iOSUIView動畫
- 自定義present和dismiss的轉場動畫動畫
- 自定義TabBar動畫效果 - 頁面轉場(Swift)tabBar動畫Swift
- iOS自定義 Transitions 動畫總結iOS動畫
- jQuery動畫—自定義動畫animate()jQuery動畫
- iOS偽轉場動畫iOS動畫
- 關於自定義轉場動畫,我都告訴你。動畫
- Flutter動畫之自定義動畫元件-FlutterLayoutFlutter動畫元件
- 輕鬆學習之二——iOS利用Runtime自定義控制器POP手勢動畫iOS動畫
- 搞定動畫之 JQuery 中的自定義動畫動畫jQuery
- Java 給PPT新增動畫效果(預設動畫/自定義動畫)Java動畫
- 自定義過渡動畫動畫
- 自定義動畫方法animate()動畫
- 自定義波紋動畫動畫
- 【Jquery】jquery 自定義動畫jQuery動畫
- iOS專案開發實戰——配置自定義動畫iOS動畫
- 萬彩動畫大師教程 | 自定義動畫函式動畫函式
- iOS CATransform3D的轉場動畫iOSORM3D動畫
- 動畫函式的繪製及自定義動畫函式動畫函式
- Android動畫效果之自定義ViewGroup新增布局動畫Android動畫View
- jQuery的三組基本動畫與自定義動畫總結jQuery動畫
- Flutter自定義CupertinoPageRoute進入動畫Flutter動畫
- 屬性動畫:如何自定義View動畫View
- Android SeekBar 自定義thumb,thumb旋轉動畫效果Android動畫
- 自定義 push 和 pop 實現有趣的相簿翻開效果(上)
- iOS自定義MJRefresh上拉和下拉重新整理動畫iOS動畫
- 【動畫消消樂】HTML+CSS 自定義載入動畫 061動畫HTMLCSS
- 【動畫消消樂】HTML+CSS 自定義載入動畫 062動畫HTMLCSS
- 【動畫消消樂】HTML+CSS 自定義載入動畫 065動畫HTMLCSS