【转】自定义presentviewcontroller和pushviewcontroller转场动画

自定义NavigationController动画

首先,实现一个非常简单的UINavigationController转场,一般会这么干

实现FirstViewController,加到Window上(没用storyboard和xib)

实现FirstViewController上面有个按钮,点击后push到SecondViewController

贴一下FirstViewController的关键代码,这很简单

- (void)viewDidLoad {

    [super viewDidLoad];

    //init First

    self.navigationItem.title = @"First";

    self.view.backgroundColor = [UIColor orangeColor];

    //init Second

    secondViewController = [[SecondViewController alloc] init];

    // Push

    UIButton *pushButton = [UIButton buttonWithType:UIButtonTypeSystem];

    pushButton.frame = CGRectMake(140, 200, 40, 40);

    [pushButton setTitle:@"Push" forState:UIControlStateNormal];

    [pushButton addTarget:self action:@selector(push) forControlEvents:UIControlEventTouchUpInside];

    [self.view addSubview:pushButton];

}

- (void)push

{

    [self.navigationController pushViewController:secondViewController animated:YES];

}

下面我们要自定义这个push动画

我们先实现一个push自定义动画类姑且叫做CustomPushAnimation,它实现UIViewControllerAnimatedTransitioning协议,用来定义一个非交互动画(就是动画过程中没交互)

里面也用到了UIViewControllerContextTransitioning这个协议,可以理解为转场动画的上下文,一个容器。下面是代码.h和.m,记住这是一个自定义的push的动画

#import <Foundation/Foundation.h>

#import <UIKit/UIKit.h>

@interface CustomPushAnimation : NSObject<UIViewControllerAnimatedTransitioning>

@end

#import "CustomPushAnimation.h"

@implementation CustomPushAnimation

- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext

{

    return 3.0;

}

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext

{

    //目的ViewController

    UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

    //起始ViewController

    UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];

    

    //添加toView到上下文

    [[transitionContext containerView] insertSubview:toViewController.view belowSubview:fromViewController.view];

    

    //自定义动画

    toViewController.view.transform = CGAffineTransformMakeTranslation(320, 568);

    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{

        fromViewController.view.transform = CGAffineTransformMakeTranslation(-320, -568);

        toViewController.view.transform = CGAffineTransformIdentity;

    } completion:^(BOOL finished) {

        fromViewController.view.transform = CGAffineTransformIdentity;

        // 声明过渡结束时调用 completeTransition: 这个方法

        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];

    }];

}

@end

下面在FirstViewController里面初始化CustomPush动画,并给FirstViewController添加UINavigationController代理

    //FirstViewController viewDidLoad
    self.navigationController.delegate = self;

    //init CustomPush
    customPush = [[CustomPushAnimation alloc] init];

为什么要添加代理呢?怎么用这个CustomPush动画呢?

答案就是我们要用UINavigationControllerDelegate的

– (id<UIViewControllerAnimatedTransitioning>) navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC

这个方法来选中Push转场的时候用我们自己定义的CustomPush动画。

#pragma mark - UINavigationControllerDelegate iOS7<span class="s1">非交互自定义</span>Navigation<span class="s1">转场</span>
// 动画特效

- (id<UIViewControllerAnimatedTransitioning>) navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC

{

    /**

     *  typedef NS_ENUM(NSInteger, UINavigationControllerOperation) {

     *     UINavigationControllerOperationNone,

     *     UINavigationControllerOperationPush,

     *     UINavigationControllerOperationPop,

     *  };

     */
    //push的时候用我们自己定义的customPush
    if (operation == UINavigationControllerOperationPush) {

        return customPush;

    }else{

        return nil;

    }

}

大功告成,现在就运行一下试试吧,可以发现动画像我想的一样,FirstViewController往左上角飘,SecondViewController紧随其后,这样完成了转场,这样我们就自定义了一个push的转场动画!

很有趣吧,下面照葫芦画瓢,做一个Pop动画吧!聪明的你一定知道,只需要在自定义转场的代理里面加一个if语句判断pop就好

// 动画特效

- (id<UIViewControllerAnimatedTransitioning>) navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC

{

    /**

     *  typedef NS_ENUM(NSInteger, UINavigationControllerOperation) {

     *     UINavigationControllerOperationNone,

     *     UINavigationControllerOperationPush,

     *     UINavigationControllerOperationPop,

     *  };

     */

    if (operation == UINavigationControllerOperationPush) {

        return customPush;

    }else if (operation == UINavigationControllerOperationPop) {

        return customPop;

    }else{

        return nil;

    }

}

**********************************************************************************************************************

自定义PresentViewController动画

NavigationController搞定,Present也依葫芦画瓢

工程里添加第三个ViewController 叫做ThirdViewController,然后在FirstViewController里面添加下面代码

    //init Third

    thirdViewController = [[ThirdViewController alloc] init];



    // Present

    UIButton *presentButton = [UIButton buttonWithType:UIButtonTypeSystem];

    presentButton.frame = CGRectMake(110, 400, 100, 40);

    [presentButton setTitle:@"Present" forState:UIControlStateNormal];

    [presentButton addTarget:self action:@selector(present) forControlEvents:UIControlEventTouchUpInside];

    [self.view addSubview:presentButton];

然后ThirdViewController添加一个按钮用来Dismiss自己,这个就不写了,然后运行起来实现的功能就很正常,点击Present按钮就会自下而上的一个动画推入新的ViewController,这很正常,然后现在来实现高级动画

首先,和Navigation类似,在FirstViewController我们要做一些准备工作

修改FirstViewController的协议

@interface FirstViewController ()<UINavigationControllerDelegate,UIViewControllerTransitioningDelegate>

然后和NavigationController有区别的是给自己的“下家儿”,也就是要转去的ViewController设置代理

thirdViewController.transitioningDelegate = self;

接下来就要在present时候和dismiss两个协议方法设置自定义动画,下面两个方法来于UIViewControllerTransitioningDelegate

#pragma mark - UIViewControllerTransitioningDelegate

-(id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source

{

    customPresent.animationType = AnimationTypePresent;

    return customPresent;

}

-(id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed

{

    customPresent.animationType = AnimationTypeDismiss;

    return customPresent;

}

那么,customPresent.animationType = AnimationTypePresent;这是什么东西呢?不要着急,这个就是自定义的Present动画效果,下面就讲他的实现,但是目前已经可以看到代码写起来和NavigationController的自定义动画非常像,对仗工整,都是协议,设置代理,调用代理时候使用自定义的动画。

下面定义真正的自定义动画CustomPresentAnimation.h

typedef enum {

    AnimationTypePresent,

    AnimationTypeDismiss

} AnimationType;

#import <Foundation/Foundation.h>

#import <UIKit/UIKit.h>

@interface CustomPresentAnimation : NSObject <UIViewControllerAnimatedTransitioning>

@property (nonatomic, assign) AnimationType animationType;

@end

#import "CustomPresentAnimation.h"

@implementation CustomPresentAnimation

- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext

{

    return 1.3;

}

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {

    

    UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

    UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];

    

    UIView * toView = toViewController.view;

    UIView * fromView = fromViewController.view;

    

    if (self.animationType == AnimationTypePresent) {

        

        //snapshot方法是很高效的截屏

        

        //First放下面

        UIView * snap = [fromView snapshotViewAfterScreenUpdates:YES];

        [transitionContext.containerView addSubview:snap];

        //Third放上面

        UIView * snap2 = [toView snapshotViewAfterScreenUpdates:YES];

        [transitionContext.containerView addSubview:snap2];

        

        snap2.transform = CGAffineTransformMakeTranslation(-320, 0);

        

        //进行动画

        [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 usingSpringWithDamping:0.5 initialSpringVelocity:0 options:UIViewAnimationOptionCurveLinear animations:^{

            snap2.transform = CGAffineTransformIdentity;

        } completion:^(BOOL finished) {

            //删掉截图

            [snap removeFromSuperview];

            [snap2 removeFromSuperview];

            //添加视图

            [[transitionContext containerView] addSubview:toView];

            //结束Transition

            [transitionContext completeTransition:![transitionContext transitionWasCancelled]];

        }];

        

        

    } else {

        

        //First 放下面

        UIView * snap = [toView snapshotViewAfterScreenUpdates:YES];

        [transitionContext.containerView addSubview:snap];

        

        //Third 放上面

        UIView * snap2 = [fromView snapshotViewAfterScreenUpdates:YES];

        [transitionContext.containerView addSubview:snap2];

        

        //进行动画

        [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 usingSpringWithDamping:0.5 initialSpringVelocity:0 options:UIViewAnimationOptionCurveLinear animations:^{

            snap2.transform = CGAffineTransformMakeTranslation(-320, 0);

        } completion:^(BOOL finished) {

            //删掉截图

            [snap removeFromSuperview];

            [snap2 removeFromSuperview];

            //添加视图

            [[transitionContext containerView] addSubview:toView];

            //结束Transition

            [transitionContext completeTransition:![transitionContext transitionWasCancelled]];

        }];

    }

}

@end

调用的时候就和上面代码所说一样,在UIViewControllerTransitioningDelegate代理里完成,那么自定义的Present转场就是这样了。运行一下,会发现很调皮的ThirdView在Present转场的时候从左面弹性的进入屏幕。

交互式的动画

想想iOS7的UINavigationController自带的动画,可以用手指慢慢往左滑动来pop出一个页面,动画是随着手指的移动交互式呈现的,这个就很有趣了,那么除了系统帮我们实现的,我们自己也可以自定义交互式动画了。

下面就来给我们的App添加一个Pan手势,给之前实现的Present动画增加一个交互式功能。

FirstViewController的viewdidload添加pan手势

    UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(didClickPanGestureRecognizer:)];

    [self.navigationController.view addGestureRecognizer:panRecognizer];

然后执行这个方法,在手势执行的时候刷新交互状态,核心方法是updateInteractiveTransition

#pragma mark - 手势交互的主要实现--->UIPercentDrivenInteractiveTransition

- (void)didClickPanGestureRecognizer:(UIPanGestureRecognizer*)recognizer

{

    UIView* view = self.view;

    if (recognizer.state == UIGestureRecognizerStateBegan) {

        interactionController = [[UIPercentDrivenInteractiveTransition alloc] init];

        [self presentViewController:thirdViewController animated:YES completion:nil];

    } else if (recognizer.state == UIGestureRecognizerStateChanged) {

        CGPoint translation = [recognizer translationInView:view];

        CGFloat distance = fabs(translation.x / CGRectGetWidth(view.bounds));

        [interactionController updateInteractiveTransition:distance];

    } else if (recognizer.state == UIGestureRecognizerStateEnded) {

        CGPoint translation = [recognizer translationInView:view];

        CGFloat distance = fabs(translation.x / CGRectGetWidth(view.bounds));

        if (distance > 0.5) {

            [interactionController finishInteractiveTransition];

        } else {

            [interactionController cancelInteractiveTransition];

        }

        interactionController = nil;

    }

}

这里需要提一下,初始化下面的代码,用于交互式转场,

//通过 UIViewControllerInteractiveTransitioning 协议进行交互转场。

    UIPercentDrivenInteractiveTransition* interactionController;

然后根据UIViewControllerTransitioningDelegate这个协议添加两个方法,来说明我们使用交互式转场

- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator

{

    return interactionController;

}

- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator

{

    return nil;

}


猜你喜欢

转载自blog.csdn.net/ck_19900710/article/details/50481866