天天品嘗iOS7甜點 Day14:Interactive View Controller Transitions

為模態(tài)視圖控制器添加交互式視圖轉(zhuǎn)換

參考

這篇文章可能會用到之前兩篇文章的知識:

Flip Transition Animation - 翻轉(zhuǎn)過渡效果

默認(rèn)的模態(tài)視圖翻轉(zhuǎn)效果

自定義模態(tài)視圖翻轉(zhuǎn)效果

實現(xiàn)方式:

  1. 視圖層次結(jié)構(gòu)

  2. 自定義漸變動畫類,遵守 UIViewControllerAnimatedTransitioning 協(xié)議以實現(xiàn)具體的動畫效果:

    // ********************************************
    // SCFlipAnimation.h
    #import <Foundation/Foundation.h>
    
    /**
     遵守<UIViewControllerAnimatedTransitioning>協(xié)議的自定義漸變動畫
     */
    @interface SCFlipAnimation : NSObject <UIViewControllerAnimatedTransitioning>
    
    /** 確定頁面的翻轉(zhuǎn)方向 */
    @property (nonatomic, assign) BOOL dismissal;
    
    @end
    
    // ********************************************
    // SCFlipAnimation.m
    #import "SCFlipAnimation.h"
    
    @implementation SCFlipAnimation
    
    - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
         {
        // 獲取各自的視圖控制器
        UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
        UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
        
        // 獲取視圖
        UIView *containerView = [transitionContext containerView];
        UIView *fromView = fromVC.view;
        UIView *toView = toVC.view;
        
        // 添加 toView 到容器視圖
        [containerView addSubview:toView];
        
        // 設(shè)置 frames
        CGRect initialFrame = [transitionContext initialFrameForViewController:fromVC];
        fromView.frame = initialFrame;
        toView.frame = initialFrame;
        
        // 構(gòu)建3D轉(zhuǎn)換需要的視角
        CATransform3D transform = CATransform3DIdentity;
        transform.m34 = -1/CGRectGetHeight(initialFrame);
        containerView.layer.sublayerTransform = transform;
        
        // 翻轉(zhuǎn)方向
        CGFloat direction = self.dismissal ? -1.0 : 1.0;
        
        // toView 反向翻轉(zhuǎn) pi/2
        toView.layer.transform = CATransform3DMakeRotation(-direction * M_PI_2, 1, 0, 0);
        
        // 關(guān)鍵幀動畫
        [UIView animateKeyframesWithDuration:[self transitionDuration:transitionContext]
                                       delay:0.0
                                     options:0
                                  animations:^{
                                      // First half is rotating in
                [UIView addKeyframeWithRelativeStartTime:0.0
                                        relativeDuration:0.5
                                              animations:^{
            // fromView 正向翻轉(zhuǎn) pi/2
            fromView.layer.transform = CATransform3DMakeRotation(direction * M_PI_2, 1, 0, 0);
                                              }];
                [UIView addKeyframeWithRelativeStartTime:0.5
                                        relativeDuration:0.5
                                              animations:^{
            // toView 還原
            toView.layer.transform = CATransform3DMakeRotation(0, 1, 0, 0);
                                              }];
                                  } completion:^(BOOL finished) {
                                      [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
                                  }];
    }
    
    - (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
         {
        return 1.0;
    }
    
    @end
    
  3. First View Controller,遵守 UIViewControllerTransitioningDelegate 協(xié)議

    @interface SCViewController () <UIViewControllerTransitioningDelegate> {
        SCFlipAnimation *_flipAnimation;
    }
    
    @end
    
    @implementation SCViewController
    
    #pragma mark - Lifecycle
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
     // Do any additional setup after loading the view, typically from a nib.
        _flipAnimation = [SCFlipAnimation new];
    }
    
    #pragma mark - 
    
    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
    {
        if([segue.destinationViewController isKindOfClass:[SCModalViewController class]]) {
            SCModalViewController *vc = (SCModalViewController *)segue.destinationViewController;
        }
    }
    
    #pragma mark - UIViewControllerTransitioningDelegate
    
    // 彈出模態(tài)視圖控制器
    // 返回遵守 UIViewControllerAnimatedTransitioning 協(xié)議的實例
    - (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
    {
        _flipAnimation.dismissal = NO;
        return _flipAnimation;
    }
    
    // 消失模態(tài)視圖控制器
    // 返回遵守 UIViewControllerAnimatedTransitioning 協(xié)議的實例
    - (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
    {
        _flipAnimation.dismissal = YES;
        return _flipAnimation;
    }
    
  4. Model View Controller 實現(xiàn)按鈕方法

    // 使模態(tài)視圖控制器消失
    - (IBAction)handleDismissPressed:(id)sender {
        [self dismissViewControllerAnimated:YES completion:NULL];
    }
    

Interactive transitioning - 交互式轉(zhuǎn)換

用手勢上滑、下滑操作實現(xiàn)視圖轉(zhuǎn)換

  1. 自定義 UIPercentDrivenInteractiveTransition 子類對象,用于手勢處理

    // ********************************************
    // SCFlipAnimationInteractor.h
    #import <UIKit/UIKit.h>
    #import "SCInteractiveTransitionViewControllerDelegate.h"
    
    @interface SCFlipAnimationInteractor : UIPercentDrivenInteractiveTransition
    
    @property (nonatomic, strong, readonly) UIPanGestureRecognizer *gestureRecogniser;
    @property (nonatomic, assign, readonly) BOOL interactionInProgress;
    
    // 實現(xiàn) SCInteractiveTransitionViewControllerDelegate 自定義交互式轉(zhuǎn)場協(xié)議的任意視圖控制器
    @property (nonatomic, weak) UIViewController<SCInteractiveTransitionViewControllerDelegate> *presentingVC;
    
    @end
    
    // ********************************************
    // SCFlipAnimationInteractor.m
    #import "SCFlipAnimationInteractor.h"
    
    @interface SCFlipAnimationInteractor ()
    
    @property (nonatomic, strong, readwrite) UIPanGestureRecognizer *gestureRecogniser;
    @property (nonatomic, assign, readwrite) BOOL interactionInProgress;
    
    @end
    
    @implementation SCFlipAnimationInteractor
    
    - (instancetype)init
    {
        self = [super init];
        if (self) {
            self.gestureRecogniser = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
        }
        return self;
    }
    
    #pragma mark - Gesture recognition
    // 手勢操作的方法
    - (void)handlePan:(UIPanGestureRecognizer *)pgr
    {
        CGPoint translation = [pgr translationInView:pgr.view];
        // 手勢百分比,用百分比的方式來激活指定的動畫過程。
        CGFloat percentage  = fabs(translation.y / CGRectGetHeight(pgr.view.bounds));
        switch (pgr.state) {
            case UIGestureRecognizerStateBegan: {
                // 手勢開始,執(zhí)行翻轉(zhuǎn)動畫
                self.interactionInProgress = YES;
                // 向目標(biāo)對象傳達(dá)執(zhí)行動畫的命令
                [self.presentingVC proceedToNextViewController];
                break;
            }
    
            case UIGestureRecognizerStateChanged: {
                // 手勢進(jìn)行中,更新翻轉(zhuǎn)進(jìn)度
                [self updateInteractiveTransition:percentage];
                break;
            }
                
            case UIGestureRecognizerStateEnded: {
                // 手勢結(jié)束,分進(jìn)度值處理
                if(percentage < 0.5) {
                    // 1.進(jìn)度小于0.5,取消轉(zhuǎn)場動畫
                    [self cancelInteractiveTransition];
                } else {
                    // 2.進(jìn)度大于0.5,自動完成轉(zhuǎn)場動畫
                    [self finishInteractiveTransition];
                }
                self.interactionInProgress = NO;
                break;
            }
                
            case UIGestureRecognizerStateCancelled: {
                // 取消手勢
                [self cancelInteractiveTransition];
                self.interactionInProgress = NO;
                break;
            }
                
            default:
                break;
        }
    }
    
    @end
    
  2. 自定義交互協(xié)議

#import <Foundation/Foundation.h>

// 1.該協(xié)議的作用:
// 將手勢與目標(biāo)視圖相關(guān)聯(lián),手勢開始時,自定義的手勢處理類需要向目標(biāo)視圖發(fā)送執(zhí)行視圖轉(zhuǎn)場的命令。
// 2.為什么要將手勢與目標(biāo)視圖關(guān)聯(lián)呢?
// 因為手勢是添加到 window 上的,這樣就可以響應(yīng)預(yù)期的行為。
// 3.為什么手勢要添加到 window 上而不是視圖上?
// 因為動畫發(fā)生的時候,視圖控制器中的視圖將會被移除,因此手勢就沒有辦法響應(yīng)行為了。
@protocol SCInteractiveTransitionViewControllerDelegate <NSObject>

- (void)proceedToNextViewController;

@end

?

  1. First View Controller

    // ********************************************
    // SCViewController.h
    #import <UIKit/UIKit.h>
    
    @interface SCViewController : UIViewController
    
    @end
    
    // ********************************************
    // SCViewController.m
    #import "SCViewController.h"
    #import "SCModalViewController.h"
    #import "SCFlipAnimationInteractor.h"
    #import "SCFlipAnimation.h"
    #import "SCInteractiveTransitionViewControllerDelegate.h"
    
    @interface SCViewController () <SCInteractiveTransitionViewControllerDelegate, UIViewControllerTransitioningDelegate> {
        SCFlipAnimationInteractor *_animationInteractor;
        SCFlipAnimation *_flipAnimation;
    }
    
    @end
    
    @implementation SCViewController
    
    #pragma mark - Lifecycle
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
     // Do any additional setup after loading the view, typically from a nib.
        _animationInteractor = [SCFlipAnimationInteractor new];
        _flipAnimation = [SCFlipAnimation new];
    }
    
    - (void)viewDidAppear:(BOOL)animated
    {
        // 添加手勢識別器到window對象中。
        // 這是因為動畫發(fā)生的時候,視圖控制器中的視圖將會被移除,因此手勢就沒有辦法響應(yīng)行為了。
        // 把它添加到window上面,將會確保我們期待的行為。
        if (![self.view.window.gestureRecognizers containsObject:_animationInteractor.gestureRecogniser]) {
            [self.view.window addGestureRecognizer:_animationInteractor.gestureRecogniser];
        }
        // 在第一頁中,交互的接收者指向 self 自身
        _animationInteractor.presentingVC = self;
    }
    
    #pragma mark - 
    
    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
    {
        if([segue.destinationViewController isKindOfClass:[SCModalViewController class]]) {
            // Set the delegate
            SCModalViewController *vc = (SCModalViewController *)segue.destinationViewController;
            vc.transitioningDelegate = self;
            // 模態(tài)視圖的手勢交互器也指向這個類的手勢交互器(弱引用)
            // 也就是說,這兩個視圖的手勢交互器指向的是同一個對象
            vc.interactor = _animationInteractor;
        }
    }
    
    #pragma mark - SCInteractiveTransitionViewControllerDelegate
    
    - (void)proceedToNextViewController
    {
        [self performSegueWithIdentifier:@"displayModal" sender:self];
    }
    
    #pragma mark - UIViewControllerTransitioningDelegate
    
    // 彈出模態(tài)視圖控制器
    - (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
    {
        _flipAnimation.dismissal = NO;
        return _flipAnimation;
    }
    
    // 消失模態(tài)視圖控制器
    - (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
    {
        _flipAnimation.dismissal = YES;
        return _flipAnimation;
    }
    
    // 交互式轉(zhuǎn)場方法1:展示
    // 返回一個實現(xiàn) UIViewControllerInteractiveTransitioning 協(xié)議的對象
    - (id<UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id<UIViewControllerAnimatedTransitioning>)animator
    {
        return _animationInteractor.interactionInProgress ? _animationInteractor : nil;
    }
    
    // 交互式轉(zhuǎn)場方法2:消失
    // 返回一個實現(xiàn) UIViewControllerInteractiveTransitioning 協(xié)議的對象
    - (id<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator
    {
        return _animationInteractor.interactionInProgress ? _animationInteractor : nil;
    }
    
    @end
    
  2. Model View Controller

    // ********************************************
    // SCModalViewController.h
    #import <UIKit/UIKit.h>
    #import "SCInteractiveTransitionViewControllerDelegate.h"
    #import "SCFlipAnimationInteractor.h"
    
    @interface SCModalViewController : UIViewController <SCInteractiveTransitionViewControllerDelegate>
    
    - (IBAction)handleDismissPressed:(id)sender;
    
    @property (nonatomic, weak) SCFlipAnimationInteractor *interactor;
    
    @end
    
    // ********************************************
    // SCModalViewController.m
    #import "SCModalViewController.h"
    
    @implementation SCModalViewController
    
    #pragma mark - Lifecycle
    
    - (void)viewDidAppear:(BOOL)animated
    {
        // 重新設(shè)置哪一個視圖控制器是交互式轉(zhuǎn)場的接收者
        // 在模態(tài)頁面中,交互的接收者又重新設(shè)置為模態(tài)頁面自身
        self.interactor.presentingVC = self;
    }
    
    #pragma mark - IBActions
    
    // 使模態(tài)視圖控制器消失
    - (IBAction)handleDismissPressed:(id)sender {
        [self dismissViewControllerAnimated:YES completion:NULL];
    }
    
    #pragma mark - SCInteractiveTransitionViewControllerDelegate
    
    - (void)proceedToNextViewController
    {
        [self dismissViewControllerAnimated:YES completion:NULL];
    }
    
    @end
    

The End.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容