美高梅在线官网 5

美高梅在线官网:Pop动画教程,iOS学习笔记

iOS

这篇笔记翻译自raywenderlick网站的过渡动画的一篇文章,原文用的swift,由于考虑到swift版本变动以及一些语法兼容问题,这里我还是用Objective-C进行了改写,没有逐字翻译,加了部分自己的理解。原文链接Creating
Custom UIViewController
Transitions。过渡动画有些地方也是翻译成转场动画,即从一个视图控制器切到另一个视图控制器,本文以过渡来译。

本文资料来自http://www.appcoda.com/facebook-pop-framework-intro/
如有错误欢迎各位指出

 原来世界可以这样动 原来优雅可以这样画

美高梅在线官网:Pop动画教程,iOS学习笔记。1 前言

iOS自身就提供了很多针对UIViewController的过渡动画,比如Cover Vertically(从下往上弹出效果)Cross Dissolve(淡入淡出效果)Partial Curl(书卷翻页效果)等。如图1就是本文用到的示例中的iOS原生的Cover Vertically效果的展示。

美高梅在线官网 1

图1 Cover Vertically效果展示

为了自己的APP更有个性,自带的效果往往不够酷炫,所以需要自定义过渡动画,通过这篇文章,我们会GET到下面几个技能:

  • 过渡动画API的构建。
  • 使用自定义的过渡动画来present和dismiss一个视图控制器。present过渡会在应用视图层级结构中添加一个新的视图控制器,而dismiss过渡会从层级结构中删除一个或多个视图控制器。
  • 学会使用交互式过渡动画。

在我们开始的示例代码中,还没有加入自定义过渡动画,已经有的内容是一个PageViewController,里面装载的为CardViewController(内容为一个UIView+一个Label用于展示图片描述),点击CardViewController里面的卡片,会切换到RevealViewController(包含一个Label展示图片名字,一个Image
View展示宠物图片,一个按钮用于返回到卡片视图)。而我们最终要达到的效果如图2所示:

美高梅在线官网 2

美高梅在线官网:Pop动画教程,iOS学习笔记。图2 最终效果图

如果你使用 CocoaPods,你可以添加下面这句话到你的工程中的Podfile文件中;

等最美的晚霞 等故事长大 用手中的流沙画一个你 曾说过的永远我们一定不会差
也答应说好的未来绝不会重画 原来幸福可以这么优雅

2 过渡动画API探究

过渡动画API涉及到的一些角色如图3所示,下面分开介绍:

美高梅在线官网 3

图3 过渡动画API角色

pod ‘pop’

为女神 呐喊吧

2.1 过渡动画API中的角色

本节内容对过渡动画API中的各个角色进行说明,包含的角色参照图3。

如果你没有CocoaPods,你能下载Pop这个框架在https://github.com/facebook/pop
之后添加pop这个文件夹到你的工程中,并且确保你的工程中的“Other Linker
Flags”选项已经添加了“-lc++”;

在这里支持下李安的那首<逝去的爱>
 
开篇已经结束 让我们开始深入的了解吧.

美高梅在线官网:Pop动画教程,iOS学习笔记。2.1.1 过渡动画代理(Transitioning Delegate)

每个View
Controller都有一个transitionDelegate属性,这个代理实现了UIViewControllerTransitioningDelegate协议。

每当你要present或者dismiss一个View
Controller的时候,UIKit会去过渡动画代理中查询需要使用的动画效果。实际项目中,我们可以设置代理为自定义的类来返回我们需要的自定义的动画效果。

5BAD58AF-BE99-4A65-954D-90C7CD300FB9.png

本期内容

2.1.2 动画控制器(Animation Controller)

动画控制器是实现了UIViewControllerAnimatedTransitioning协议的用于执行过渡动画的对象。

使用Pop

首先导入头文件

#import "POP.h"

GitHub地址 
会随时更新, 欢迎大家一起学习, 如果有好的建议,
好的思想请告诉我.我的路上有您.请记得右上角给我star.

2.1.3 过渡动画上下文对象(Transitioning Context)

上下文对象实现了UIViewControllerContextTransitioning协议,在动画过程中是至关重要的,它封装了所有的参与过渡动画的View
Controllers的信息。不过我们不用写代码实现它,在动画控制器里面,过渡动画执行的时候,我们的函数会接收到一个上下文对象作为参数并从中获取相关View
Controller的信息。

例子一:UITableViewCell Animation

pop-animation-1-1.gif

我们创建一个动画在tableViewCell上,首先添加一个放大的动画当我们点击cell的时候,当我们手离开屏幕的时候,我们用将cell上缩回原先的大小用spring
animation;

重写cell中的setHighlighted方法,代码如下:

- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
    [super setHighlighted:highlighted animated:animated];
    if (self.highlighted) {
        POPBasicAnimation *scaleAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPViewScaleXY];
        scaleAnimation.duration = 0.1;
        scaleAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(1, 1)];
        [self.textLabel pop_addAnimation:scaleAnimation forKey:@"scalingUp"];



    } else {
        POPSpringAnimation *sprintAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPViewScaleXY];
        sprintAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(0.9, 0.9)];
        sprintAnimation.velocity = [NSValue valueWithCGPoint:CGPointMake(2, 2)];
        sprintAnimation.springBounciness = 20.f;
        [self.textLabel pop_addAnimation:sprintAnimation forKey:@"springAnimation"];
    }
}

Pop为我们提供许多创建动画的预定义属性,你能找到这些属性在“POPAnimatableProperty.h”文件中;

登录动画

2.2 过渡动画流程

    1. 你触发一个过渡动作。可以通过编码或者segue来触发。
    1. UIKit询问要过渡到的目的视图控制器它是否有自定义的过渡动画代理。如果没有,则UIKit将使用iOS自带的过渡动画。
    1. 然后,UIKit通过过渡动画代理,获取到动画控制器。比如通过
      animationControllerForPresentedController(_:presentingController:sourceController:)方法获取到动画控制器,如果返回空,则使用默认的动画控制器。
    1. 一旦找到了动画控制器,UIKit构建上下文对象。
    1. 接着,UIKit通过动画控制器的
      transitionDuration(_:)方法获取动画执行时长。
    1. 再接着调用动画控制器的animateTransition(_:)完成过渡动画。
    1. 最后动画控制器调用上下文对象的completeTransition(_:)方法指示动画完成。图4是官方文档的一个过渡动画的API角色示意图。

美高梅在线官网 4

图4 过渡动画角色示意图2

例子二:Animating a Like Button

pop-animation-2.gif

创建一个controller在storyBoard中,在controller中创建一个UITextField,之后添加一个send和like按钮,like按钮在send按钮的上面,默认的情况下我们展示like按钮,当用户输入文字在textField中我们隐藏like按钮,展示send按钮用一个动画;
创建一个名为FacebookButtonAnimationViewController的类
继承UIViewController;在这个类中添加like和send按钮,还有textField的变量;

@interface FacebookButtonAnimationViewController ()
@property (weak, nonatomic) IBOutlet UITextField *msgTextField;
@property (weak, nonatomic) IBOutlet UIButton *likeButton;
@property (weak, nonatomic) IBOutlet UIButton *sendButton;

@end

实现UITextField的代理方法:

-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string

在这个方法中我们可以监控用户输入和删除文字,内部的实现如下:

-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    NSString *comment;
    if(range.length == 0)
    {
        comment = [NSString stringWithFormat:@"%@%@", textField.text, string];
    }
    else
    {
        comment = [textField.text substringToIndex:textField.text.length - range.length];
    }

    if (comment.length == 0) {
        //Show like
        [self showLikeButton];
    }
    else
    {
        //Show Send
        [self showSendButton];
    }
    return YES;
}

之后我们实现showLikeButton和showSendButton

-(void)showLikeButton
{
    self.likeButton.hidden = NO;
    self.sendButton.hidden = YES;

    POPSpringAnimation *spin = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerRotation];

    spin.fromValue = @(M_PI / 4);
    spin.toValue = @(0);
    spin.springBounciness = 20;
    spin.velocity = @(10);
    [self.likeButton.layer pop_addAnimation:spin forKey:@"likeAnimation"];
}

-(void)showSendButton
{
    if (self.sendButton.isHidden) {

        self.likeButton.hidden = YES;
        self.sendButton.hidden = NO;
        POPSpringAnimation *sprintAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPViewScaleXY];

        sprintAnimation.velocity = [NSValue valueWithCGPoint:CGPointMake(8, 8)];
        sprintAnimation.springBounciness = 20.f;
        [self.sendButton pop_addAnimation:sprintAnimation forKey:@"sendAnimation"];
    }
}

过渡动画

2.3 实现Presentation过渡动画

我们总共要实现三个动画效果,一个是Presentation过渡动画,一个是dismiss过渡动画,另外还有一个交互动画。

Presentation的效果主要如下:

  • 点击卡片的时候,卡片翻转显示第二个视图,且第二个视图初始大小跟卡片大小一样。
  • 第二个视图放大至整个屏幕大小。

例子三:Wrong Password Animation

pop-animation-3-2.gif

首先在storyBoard中新建一个controller,在controller中添加textField和一个button;
接下来创建一个继承UIViewControll名位WrongPasswordViewController的类,在类中创建一个UITextField的变量与storyBoard中的textField关联,命名为passwordTextField;

@interface WrongPasswordViewController ()
@property (weak, nonatomic) IBOutlet UITextField *passwordTextField;

@end

之后我们创建一个名位login的方法为Login
Button,在这个方法中我们校验密码是否正确,如果不正确我们为textField添加一个摇晃动画;

-(void)login{
    POPSpringAnimation *shake = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerPositionX];

    shake.springBounciness = 20;
    shake.velocity = @(3000);

    [self.passwordTextField.layer pop_addAnimation:shake forKey:@"shakePassword"];
}

列表视图出现动画

2.3.1 创建Presentation动画控制器

我们创建一个名为FlipPresentAnimationController的类来完成Presentation动画效果,这个类在我们上面说的角色中就是动画控制器。

核心代码如下,代码中有注解:

/*设置动画时长函数*/
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
    return 2.0;
}

/*执行动画的函数*/
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
    //1 上下文对象transitionContext包含了参与过渡动画的视图
    // 和视图控制器信息,可以通过对应的参数获取。
    CardViewController *fromVC = (CardViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewKey];
    UIView *containerView = [transitionContext containerView];
    RevealViewController *toVC = (RevealViewController *)[transitionContext viewControllerForKey:UITransitionContextToViewKey];

    //2 设置过渡目的视图的初始大小和结束大小。
    //   初始大小为第一个视图的卡片的大小,结束大小为整个屏幕大小。
    BOOL hasViewForKey = [transitionContext respondsToSelector:@selector(viewForKey:)];
    UIView *fromView = hasViewForKey ? [transitionContext viewForKey:UITransitionContextFromViewKey] : fromVC.view;
    UIView *toView = hasViewForKey ? [transitionContext viewForKey:UITransitionContextToViewKey] : toVC.view;
    CGRect initialFrame = self.originFrame;    
    CGRect finalFrame = hasViewForKey? toView.frame : [transitionContext finalFrameForViewController:toVC];

    //3 获取一个目的视图的一个快照。设置初始frame为initFrame。
    UIView *snapshot = [toView snapshotViewAfterScreenUpdates:YES];
    snapshot.frame = initialFrame;
    snapshot.layer.cornerRadius = 25;
    snapshot.layer.masksToBounds = YES;

    //4 containerView加入目的视图和快照视图,并先隐藏目的视图。
    //   我们的动画都在containerView来实现。
    [containerView addSubview:toView];
    [containerView addSubview:snapshot];
    toView.hidden = YES;

    //5 设置动画视角,将快照视图先沿Y轴旋转到PI/2的位置。
    [AnimationHelper persipectiveTransformForContainerView:containerView];
    snapshot.layer.transform = [AnimationHelper yRotation:M_PI_2]; 

    CGFloat duration = [self transitionDuration:transitionContext];

    [UIView animateKeyframesWithDuration:duration delay:0 options:UIViewKeyframeAnimationOptionCalculationModeCubic animations:^{
        [UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:1.0/3 animations:^{
            //6 将第一个视图旋转到-PI/2的位置,方向是顺时针
            fromView.layer.transform = [AnimationHelper yRotation:-M_PI_2]; 
        }];

        [UIView addKeyframeWithRelativeStartTime:1.0/3 relativeDuration:1.0/3 animations:^{
            //7 将快照视图从PI/2的位置旋转到轴线位置,也是顺时针。正好接上6的旋转效果。
            snapshot.layer.transform = [AnimationHelper yRotation:0.0];
        }];

        [UIView addKeyframeWithRelativeStartTime:2.0/3 relativeDuration:1.0/3 animations:^{
            //8 将快照视图的frame放大至整个屏幕。
            snapshot.frame = finalFrame;
        }];
        } completion:^(BOOL finished){
            toView.hidden = NO; //显示目的视图
            fromView.layer.transform = [AnimationHelper yRotation:0.0]; //恢复第一个视图的位置
            [snapshot removeFromSuperview]; //移除快照视图
            [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; //通知UIKit动画执行完成
        }
     ];
}

额外说明几点:

    1. 注释2这段代码跟原文的swift的有点不一样,直接通过transitionContext viewControllerForKey:UITransitionContextToViewKey等函数取到的View
      Controller发现是nil,这样就没法取到动画过程中的视图信息。而通过transitionContext viewForKey:UITransitionContextToViewKey取到的视图是正常的,看网上资料说可能是ios8的BUG,没有确切资料可以确认,如果是其他设置问题,麻烦大虾们告知一下。
    1. 关于旋转方向的问题,通过上一篇笔记我们总结了三维视图中沿Y轴旋转的正反方向,正方向为逆时针。因此注释5中我们的快照视图显示逆时针的转到了PI/2的位置,而注释6会先将第一个视图转到-PI/2的位置,动画中的旋转方向是以距离最近来旋转,因此第一个视图会顺时针旋转PI/2,然后快照视图也是顺时针旋转PI/2,最后再试快照视图放大到整个屏幕。
    1. 最后的completeTransition方法调用是必须的,如果不调用的话,动画结束后目的视图将无法接受事件响应。

例子四:Custom View Controller Transition

pop-animation-4.gif

这个例子将教你们怎么跳转到一个控制器用自定义动画,我们在控制器中建一个按钮“present”,当用户点击这个按钮,app跳转到另一个控制器中用一个自定义动画,从iOS7开始,你能够自定义控制器的过渡动画用UIViewController的transitioningDelegate.在这个代理中,我们要实现UIViewControllerTransitioningDelegate的代理方法,还有提供过渡动画的实现;

首先,创建在storyBoard中创建俩个controller和俩个新类,命名为
“CustomVCTransitionViewController”和 “CustomModalViewController”
UI设计如下:

custom-view-controller-ui-hd.jpg

现在我们实现CustomVCTransitionViewController中Present按钮的点击,首先是添加UIViewControllerTransitioningDelegate的声明;
在CustomVCTransitionViewController.m 我们引入一下头文件:

#import "CustomModalViewController.h"

#import "PresentingAnimationController.h"
#import "DismissingAnimationController.h"

接下来实现Present按钮的action和代理方法:

- (IBAction)didClickOnPresent:(id)sender {

    CustomModalViewController *modalVC = [self.storyboard instantiateViewControllerWithIdentifier:@"customModal"];


    modalVC.transitioningDelegate = self;

    modalVC.modalPresentationStyle = UIModalPresentationCustom;

    [self.navigationController presentViewController:modalVC animated:YES completion:nil];
}

#pragma mark - UIViewControllerTransitionDelegate -

- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
    return [[PresentingAnimationController alloc] init];
}

- (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
    return [[DismissingAnimationController alloc] init];
}

当Present按钮被点击的时候,didClickOnPresent会响应点击事件,在这个方法中我们初始化CustomModalViewController,并且设置过度代理方法对于当前这个controller;
我们必须实现
UIViewControllerTransitioningDelegate中俩个必须实现的代理方法,animationControllerForPresentedController方法返回跳转到CustomModalViewController的过渡动画.相反,animationControllerForDismissedController返回动画为了要dismiss的控制器;
现在我们创建一个继承NSObject的新类叫做PresentingAnimationController,在这个类中我们遵守UIViewControllerAnimatedTransitioning协议.
在PresentingAnimationController.m中我们添加下面的方法:

- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
    return 0.5f;
}



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

    UIView *fromView = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey].view;
    fromView.tintAdjustmentMode = UIViewTintAdjustmentModeDimmed;
    fromView.userInteractionEnabled = NO;

    UIView *toView = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view;
    toView.frame = CGRectMake(0,
                              0,
                              CGRectGetWidth(transitionContext.containerView.bounds) - 100.f,
                              CGRectGetHeight(transitionContext.containerView.bounds) - 280.f);
    CGPoint p = CGPointMake(transitionContext.containerView.center.x, -transitionContext.containerView.center.y);
    toView.center = p;

    [transitionContext.containerView addSubview:toView];

    POPSpringAnimation *positionAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerPositionY];
    positionAnimation.toValue = @(transitionContext.containerView.center.y);
    positionAnimation.springBounciness = 10;
    [positionAnimation setCompletionBlock:^(POPAnimation *anim, BOOL finished) {
        [transitionContext completeTransition:YES];
    }];

    POPSpringAnimation *scaleAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY];
    scaleAnimation.springBounciness = 20;
    scaleAnimation.fromValue = [NSValue valueWithCGPoint:CGPointMake(1.2, 1.4)];

    [toView.layer pop_addAnimation:positionAnimation forKey:@"positionAnimation"];
    [toView.layer pop_addAnimation:scaleAnimation forKey:@"scaleAnimation"];


}

第一个方法返回过渡时间,为了实现动画,我们实现animateTransition方法,这里我们将实现怎么去跳转。

现在我们创建一个继承NSObject的新类叫做DismissingAnimationController,在这个类中我们遵守UIViewControllerAnimatedTransitioning协议.
相似,我们在DismissingAnimationController.m添加如下方法:

- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
    return 0.5f;
}

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{
    UIView *toView = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view;
    toView.tintAdjustmentMode = UIViewTintAdjustmentModeNormal;
    toView.userInteractionEnabled = YES;

    UIView *fromView = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey].view;


    POPBasicAnimation *closeAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerPositionY];
    closeAnimation.toValue = @(-fromView.layer.position.y);
    [closeAnimation setCompletionBlock:^(POPAnimation *anim, BOOL finished) {
        [transitionContext completeTransition:YES];
    }];

    POPSpringAnimation *scaleDownAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY];
    scaleDownAnimation.springBounciness = 20;
    scaleDownAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(0, 0)];

    [fromView.layer pop_addAnimation:closeAnimation forKey:@"closeAnimation"];
    [fromView.layer pop_addAnimation:scaleDownAnimation forKey:@"scaleDown"];

}

来到CustomModalViewController.m中,更新viewDidLoad方法并且实现didClickOnClose方法:

-(void)viewDidLoad
{
    [super viewDidLoad];

    self.view.layer.cornerRadius = 8.f;
}

- (IBAction)didClickOnClose:(id)sender {
    [self dismissViewControllerAnimated:YES completion:nil];
}

其它参考资料
:http://adad184.com/2015/03/11/intro-to-pop/

时间计时器动画

2.3.2 连接动画控制器

在我们的CardViewController中加入动画控制器初始化代码。这里的CardViewController实现了UIViewControllerTransitioningDelegate协议,我们要设置目的控制器的transitionDelegate为CardViewController。并实现代理的方法返回我们刚刚创建的动画控制器。代码如下:

- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
    self.flipPresentAnimationController.originFrame = self.cardView.frame;
    return self.flipPresentAnimationController;
}

// 在CardViewController的prepareSegue方法中,设置了transitionDelegate。
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    ......
    revealViewController.transitioningDelegate = self;
}

人生就像场游戏,
上王者的人在游戏里永远是最被人追捧的.所有故事(不包括爱情)都在乎的是结果,而不会在乎你付出了多少努力.唯独只有成功.

2.4 实现dismiss过渡动画

dismiss的过渡动画原理类似,不过多介绍了,实现功能是:

  • 第二个视图的图片先缩小到第一个视图的卡片大小。
  • 两个视图先后翻转,最终回到初始位置。

代码如下:

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
    CardViewController *fromVC = (CardViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewKey];
    UIView *containerView = [transitionContext containerView];
    RevealViewController *toVC = (RevealViewController *)[transitionContext viewControllerForKey:UITransitionContextToViewKey];

    BOOL hasViewForKey = [transitionContext respondsToSelector:@selector(viewForKey:)];

    UIView *fromView = hasViewForKey ? [transitionContext viewForKey:UITransitionContextFromViewKey] : fromVC.view;
    UIView *toView = hasViewForKey ? [transitionContext viewForKey:UITransitionContextToViewKey] : toVC.view;

    CGRect initialFrame = fromView.frame;
    CGRect finalFrame = self.destinationFrame;

    UIView *snapshot = [fromView snapshotViewAfterScreenUpdates:YES];
    snapshot.frame = initialFrame;
    snapshot.layer.cornerRadius = 25;
    snapshot.layer.masksToBounds = YES;

    [containerView addSubview:toView];
    [containerView addSubview:snapshot];
    fromView.hidden = YES;

    [AnimationHelper persipectiveTransformForContainerView:containerView];
    toView.layer.transform = [AnimationHelper yRotation:-M_PI_2];

    CGFloat duration = [self transitionDuration:transitionContext];

    [UIView animateKeyframesWithDuration:duration delay:0 options:UIViewKeyframeAnimationOptionCalculationModeCubic animations:^{
        [UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:1.0/3.0 animations:^{
            snapshot.frame = finalFrame;
        }];

        [UIView addKeyframeWithRelativeStartTime:1.0/3.0 relativeDuration:1.0/3.0 animations:^{
            snapshot.layer.transform = [AnimationHelper yRotation:M_PI_2];
        }];

        [UIView addKeyframeWithRelativeStartTime:2.0/3.0 relativeDuration:1.0/3.0 animations:^{
            toView.layer.transform = [AnimationHelper yRotation:0.0];
        }];
    } completion:^(BOOL finished){
        fromView.hidden = NO;
        [snapshot removeFromSuperview];
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
    }
    ];
}

当然,也少不了要在代理类中关联好dismiss的动画控制器。

1. 登录动画

2.5 实现交互动画

代码思路:

2.5.1 交互动画示例

iPhone上面的设置APP就是交互动画的一个很典型的例子,如图5所示,从左边缘开始滑动,过渡动画的进度是跟随你的手指滑动的位置来确定的(比如坐标X超过了多少则表示切换到下一个视图,否则切回上一个视图。

美高梅在线官网 5

图5 交互动画示例

第一步:继承UIButton重写UI 点击按钮时候添加POP
layerScaleSpringAnimation动画

2.5.2 交互动画原理

交互动画通过交互控制器来控制,为了实现交互动画,过渡动画代理需要额外提供一个交互控制器。交互控制器只要实现了UIViewControllerInteractiveTransitioning协议即可,它响应触控事件,通过交互控制器,动画会随着手势拖动逐渐展开而不是像之前那样直接执行完毕。

iOS提供了一个UIPercentDrivenInteractiveTransition类,它实现了UIViewControllerInteractiveTransitioning协议,我们在例子中要用到这个类。

实现点击改变Scale

2.5.3 创建交互过渡动画

创建交互动画代码如下,我们需要添加拖动事件响应,在处理事件响应的函数handleGesture中,我们根据当前手势状态和所在的位置来进行处理。注意到gestureRecognizer.view是对应的目的视图也就是RevealViewController对应的View。而它的superview则是UITransitionView这个视图。

- (void)wireToViewController:(UIViewController *)viewController {
    self.viewController = viewController;
    [self prepareGestureRecognizerInView:viewController.view];
}

- (void)prepareGestureRecognizerInView:(UIView *)view {
    UIScreenEdgePanGestureRecognizer *gesture = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action: @selector(handleGesture:)];
    gesture.edges = UIRectEdgeLeft;
    [view addGestureRecognizer:gesture];
}

- (void)handleGesture:(UIScreenEdgePanGestureRecognizer *)gestureRecognizer {
    //1 获取手势当前的坐标点
    CGPoint translation = [gestureRecognizer translationInView:gestureRecognizer.view.superview];
    CGFloat progress = (translation.x / 200);
    progress = fminf(fmaxf(progress, 0.0), 1.0);
    switch (gestureRecognizer.state) {
        //2 开始手势,设置开始交互的标识,开始触发dismissal操作。
        case UIGestureRecognizerStateBegan:
            self.interactionInProgress = YES;
            [self.viewController dismissViewControllerAnimated:YES completion:nil];
            Break;
        //3 手势拖动,判断当前的手势横轴坐标是否大于100,大于100则设置过渡动画完成。
        case UIGestureRecognizerStateChanged:
            self.shouldCompleteTransition = progress > 0.5;
            [self updateInteractiveTransition:progress];
            Break;
        //4 手势取消,设置交互状态为NO,并取消交互动画。
        case UIGestureRecognizerStateCancelled:
            self.interactionInProgress = NO;
            [self cancelInteractiveTransition];
            Break;
        //5 手势结束,根据进度来判断是取消还是完成交互动画。
        case UIGestureRecognizerStateEnded:
            self.interactionInProgress = NO;
            if (!self.shouldCompleteTransition) {
                [self cancelInteractiveTransition];
            } else {
                [self finishInteractiveTransition];
            }
        default:
            NSLog(@"Unsupported");
            break;
    }

在CardViewController中需要加入对应代码才能呈现交互动画,加入代码如下:

- (id<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator {
    return self.swipeInteractionControllers.interactionInProgress ? self.swipeInteractionControllers : nil;
}

/* 在CardViewController的prepareSegue方法中,
 设置了transitionDelegate,加入交互动画事件捕获。*/
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    ......
    revealViewController.transitioningDelegate = self;
    [self.swipeInteractionControllers wireToViewController:revealViewController];
}

至此整个动画效果完成,完整代码参见

  • Objective-C版本
  • Swift版本