iOS 視圖控制器轉場動畫/頁面切換效果/跳轉動畫 學習


一 學習 在 UINavigationController 中 push 和 pop 的轉場效果 

    (基於iOS7 以上的轉場方式)

  • 經過學習了解到,重點分三塊:
  • (1)pushAnimation: TransitionPushAnimation 進場效果動畫管理器 (NSObject並遵守UIViewControllerAnimatedTransitioning協議)
  • (2)popAnimation:   TransitionPopAnimation  出場效果動畫管理器 (NSObject並遵守UIViewControllerAnimatedTransitioning協議) 
  • (3)navigationPerformerObject: TransitionNavigationPerformer 對象操控轉場 (設置轉場動畫代理遵從 UINavigationControllerDelegate協議)

    

用同一邏輯寫了兩個效果 來看, 轉場代理方法實現差別不大. 關鍵 是 進出場效果,有人說”你的想象無限 動畫效果無限”. 所以掌握了轉場動畫基本實現基本技巧后,就應該嘗試深入研究一下動畫效果的實現.這里強調實現轉場動畫原理和實現過程

 

   1 進場效果 和 出場效果 主要實現 UIViewControllerAnimatedTransitioning 協議的兩個方法:

 

        (1)動畫切換的持續時間,以秒為單位
        
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext

 

        (2)動畫執行內容
    
- (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] 狀態沒注意導致 頁面過渡 就那么卡住了 或者 頁面空白,種種動畫效果滯澀的問題都是 狀態信號控制不好導致的

 

   2 這里需要遵從 UINavigationControllerDelegate協議的兩個方法,這兩個方法在對屏幕進行翻轉的時候對導航欄控制器變化的動畫設置 :
    
- (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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM