一 學習 在 UINavigationController 中 push 和 pop 的轉場效果
(基於iOS7 以上的轉場方式)
- 經過學習了解到,重點分三塊:
- (1)pushAnimation: TransitionPushAnimation 進場效果動畫管理器 (NSObject並遵守UIViewControllerAnimatedTransitioning協議)
- (2)popAnimation: TransitionPopAnimation 出場效果動畫管理器 (NSObject並遵守UIViewControllerAnimatedTransitioning協議)
- (3)navigationPerformerObject: TransitionNavigationPerformer 對象操控轉場 (設置轉場動畫代理遵從 UINavigationControllerDelegate協議)
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
注意要點: 判斷 動畫執行完成結果狀態
當用戶做手指跟隨操作的過程中,根據操作的finished位置 要判斷此時應該是該結束動畫 還是取消動畫.
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
下面手勢 就是 判斷用戶手勢跟隨進度 :
if ( progress > 0.5) {// 時為 完成 觸發"返回"操作
[self.interactionController finishInteractiveTransition];//transitionContext.transitionWasCancelled 會得到NO的回調
} else {// 反之 為取消 保留當前頁面
[self.interactionController cancelInteractiveTransition];//transitionContext.transitionWasCancelled 會得到YES的回調
}
起初因為 [transitionContext transitionWasCancelled] 狀態沒注意導致 頁面過渡 就那么卡住了 或者 頁面空白,種種動畫效果滯澀的問題都是 狀態信號控制不好導致的
- (nullable id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) animationController NS_AVAILABLE_IOS(7_0); - (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC NS_AVAILABLE_IOS(7_0);
通常寫法是把導航去設置和代理方法寫在對應的視圖控制器里面,這里我們創建 一個對象TransitionNavigationPerformer 遵循UINavigationControllerDelegate代理方法,這樣封裝,耦合性低,並且代碼可移植性更強.
#import <Foundation/Foundation.h> @class TransitionPushAnimation; @class TransitionPopAnimation; @interface TransitionNavigationPerformer : NSObject <UINavigationControllerDelegate> - (instancetype)initWithNav:(id)nav; //為了操控 self.navigationController @property (weak, nonatomic) UINavigationController *navigationController; //這里強調單項弱引用 @property (strong, nonatomic) TransitionPushAnimation *pushAnimation; @property (strong, nonatomic) TransitionPopAnimation *popAnimation; @property (strong, nonatomic) UIPercentDrivenInteractiveTransition *interactionController;//沒做特殊處理 就是默認系統創建 用來控制動畫效果進度狀態 @end
// // TransitionNavigationPerformer.m // SectionDemo // // Created by HF on 17/3/29. // Copyright © 2017年 HF-Liqun. All rights reserved. // #import "TransitionNavigationPerformer.h" #import "TransitionPopAnimation.h" #import "TransitionPushAnimation.h" @interface TransitionNavigationPerformer () { UIScreenEdgePanGestureRecognizer *panGesture; } @end @implementation TransitionNavigationPerformer - (instancetype)initWithNav:(id)nav { self = [super init]; if (self) { self.navigationController = nav; // 在導航控制器的視圖上添加pan手勢 --> 需要從邊緣進行拖動,在控制器轉換的時候是有用 "UIScreenEdgePanGestureRecognizer" panGesture = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)]; panGesture.edges = UIRectEdgeLeft; //主要用作返回 left [self.navigationController.view addGestureRecognizer:panGesture]; // 初始化動畫方案 self.pushAnimation = [[TransitionPushAnimation alloc] init]; self.popAnimation = [[TransitionPopAnimation alloc] init]; } return self; } - (void)dealloc { //這里目的防止取消切換效果后 導航控制器仍然會對手勢持有 [panGesture removeTarget:self action:@selector(pan:)]; self.interactionController = nil; } - (void)pan:(UIScreenEdgePanGestureRecognizer *)recognizer { UIView *view = self.navigationController.view; CGFloat progress = [recognizer translationInView:view].x / (view.bounds.size.width * 1.0); progress = MIN(1.0, MAX(0.0, progress)); if (recognizer.state == UIGestureRecognizerStateBegan) { // 創建過渡對象,彈出viewController self.interactionController = [[UIPercentDrivenInteractiveTransition alloc] init]; [self.navigationController popViewControllerAnimated:YES]; } else if (recognizer.state == UIGestureRecognizerStateChanged) { // 更新 interactive transition 的進度 [self.interactionController updateInteractiveTransition:progress]; } else if (recognizer.state == UIGestureRecognizerStateEnded || recognizer.state == UIGestureRecognizerStateCancelled) { // 完成或者取消過渡 if (progress > 0.5) { [self.interactionController finishInteractiveTransition]; } else { [self.interactionController cancelInteractiveTransition]; } self.interactionController = nil; } } #pragma mark - UINavigationControllerDelegate - (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC { if (operation == UINavigationControllerOperationPush) { return self.pushAnimation; } else if (operation == UINavigationControllerOperationPop) { return self.popAnimation; } return nil; } - (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController { // 檢查是否是我們的自定義過渡 if ([animationController isKindOfClass:[TransitionPushAnimation class]] || [animationController isKindOfClass:[TransitionPopAnimation class]]) { return self.interactionController; } else { return nil; } }
@end
3 方法調用
(1)在使用的時候 要添加self.navigationController.delegate = TransitionNavigationPerformer 對象, 不需要就設置為nil
(2)頁面viewWillDisappear 一定要立即取消代理方法 防止交互崩潰 .
參考Demo:SectionDemo
效果:
參考:
http://www.open-open.com/lib/view/open1408677710366.html
http://www.cocoachina.com/ios/20150811/12986.html
https://github.com/seedante/iOS-Note/wiki/ViewController-Transition