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連線
相關文章
- TransitionAnimation自定義轉場動畫NaN動畫
- 自定義Push/Pop和Present/Dismiss轉場
- 自定義TabBar動畫效果 - 頁面轉場(Swift)tabBar動畫Swift
- Flutter動畫之自定義動畫元件-FlutterLayoutFlutter動畫元件
- Java 給PPT新增動畫效果(預設動畫/自定義動畫)Java動畫
- 搞定動畫之 JQuery 中的自定義動畫動畫jQuery
- Android SeekBar 自定義thumb,thumb旋轉動畫效果Android動畫
- 萬彩動畫大師教程 | 自定義動畫函式動畫函式
- Android 轉場動畫Android動畫
- 動畫函式的繪製及自定義動畫函式動畫函式
- 【動畫消消樂】HTML+CSS 自定義載入動畫 065動畫HTMLCSS
- 【動畫消消樂】HTML+CSS 自定義載入動畫 062動畫HTMLCSS
- 【動畫消消樂】HTML+CSS 自定義載入動畫 061動畫HTMLCSS
- iOS自定義MJRefresh上拉和下拉重新整理動畫iOS動畫
- react-navigation自定義StackNavigator頁面跳轉動畫ReactNavigation動畫
- 萬彩動畫大師教程 | 移動動畫自定義加速度動畫
- Android自定義View播放Gif動畫AndroidView動畫
- Android 自定義View之下雨動畫AndroidView動畫
- Flutter自定義CupertinoPageRoute進入動畫Flutter動畫
- iOS 動畫之Spring動畫、Block動畫、GIF圖iOS動畫SpringBloC
- iOS 動畫iOS動畫
- 系統學習iOS動畫之四:檢視控制器的轉場動畫iOS動畫
- 【動畫消消樂】HTML+CSS 自定義載入動畫:怦然心跳 066動畫HTMLCSS
- Qt自定義動畫插值函式QT動畫函式
- Flutter 建立自定義路由過渡動畫Flutter路由動畫
- Android 自定義View:屬性動畫(六)AndroidView動畫
- [譯]Workcation App – 第一部分 . 自定義 Fragment 轉場動畫APPFragment動畫
- iOS系統導航欄自定義標題動畫跳變解析iOS動畫
- Android轉場動畫一說Android動畫
- IOS動畫使用iOS動畫
- 【動畫消消樂】HTML+CSS 自定義載入動畫 064(currentColor的妙用!)動畫HTMLCSS
- 鴻蒙HarmonyOS實戰-ArkUI動畫(頁面轉場動畫)鴻蒙UI動畫
- 鴻蒙HarmonyO實戰-ArkUI動畫(元件內轉場動畫)鴻蒙UI動畫元件
- android 自定義酷炫進度條動畫Android動畫
- 【Android】自定義ProgressView-進度條動畫AndroidView動畫
- Android 自定義帶動畫的柱狀圖Android動畫
- 「HTML+CSS」--自定義載入動畫【005】HTMLCSS動畫
- 「HTML+CSS」--自定義載入動畫【006】HTMLCSS動畫
- 「HTML+CSS」--自定義載入動畫【016】HTMLCSS動畫