iOS基础全面分析之四(动画全面分析)

作者: struggle3g | 来源:发表于2019-07-09 16:01 被阅读42次

    iOS动画

    好的应用都有一个共同的特点,那就是良好的用户体验,动画作为提升用户体验一个重要技术,在开发人员和产品设计人员手里,则应该受到足够重视。

    CoreGraphics:

    它是ios的核心图形库,平时使用最频繁的point,size,rect等这些图形,都定义在这个框架中,类名以CG开头的都属于CoreGraphics框架,它提供的都是C语言的函数接口,是可以在ios和mac os通用的。

    QuartzCore:

    这个框架的名称感觉不是很清晰,不明确,但是看它的头文件可以发现,它其实就是CoreAnimation,这个框架的头文件只包含了CoreAnimation.h

    Core animation(核心动画)

    锚点anchorPoint

    • 概念
      • anchorPoint是相对于自身layer,anchorPoint点(锚点)的值是用相对bounds的比例值来确定的。
      • anchorPoint相当于支点,可以用作旋转变化、平移、缩放,如果修改anchorPoint则layer的frame会发生改变,position不会发生改变.
      • 修改position与anchorPoint中任何一个属性都不影响另一个属性.
    • 单位:
      • 0~1
    • anchorPoint和position的关系
      • anchorPoint和position不是一个点
    • anchorPoint和position和frame的三条关系
      • 第一条关系说明:锚点不变时,frame改变,position会随着变化
      • 第二条关系说明:锚点不变时,position变化,frame会随着变化
      • 第三条关系说明:锚点改变, position不影响,frame会随着变化

    创建动画的三个步骤

    • 创建核心动画大致分为三个步骤:
      1. 初始化动画对象
      2. 设置需要修改动画的属性值
      3. 把动画添加到layer上

    隐式动画和显示动画

    当动画完成以后你看到的都是假象,你的view并没有变化,layer分为两层:

    • presentationlayer 呈现成
      • 凡是眼睛看到的都是呈现层
    • modelayer 模型层
      • 用来存储数据,等到屏幕需要刷新的时候绘制到呈现层

    显示动画

    • 通过自定义三步骤的动画都是显示动画

    隐式动画

    • 通过自创建的layer,修改其属性会默认添加一些动画效果,这些动画效果就是隐式动画
      • CALayer 默认时间 0.25s(位置、颜色、大小)
      • 必须是独立的CALayer才有隐式动画, UIView的根layer是没有的

    例如代码如下:

    
    _layer = [CALayer layer];
    _layer.frame = CGRectMake(200, 100, 100, 100);
    //可移值性
    _layer.backgroundColor = [UIColor yellowColor].CGColor;
    [self.view.layer addSublayer:_layer];
    
    _layer.frame = CGRectMake(200, 400, 100, 100);
    
    

    iOS动画框架结构

    CoreAnimation.png

    在iOS系统中给我们提供了基于Core animation这几种动画,如下图所示:

    CABasicAnimation(基本动画)

    CABasicAnimation的属性

    属性 说明
    duration 动画的时长
    repeatCount 重复的次数
    repeatDuration 设置动画的时间,在改时间内一直执行,不计算次数
    beginTime 指定动画开始时间
    timingFunction 设置动画的速度变化
    autoreverses 动画结束是否执行逆动画
    fromValue 属性的起始值
    toValue 结束的值
    byValue 改变属性相同起始值的改变量

    CABasicAnimation的Keypath属性

    apple官网动画属性

    如下常用的属性:

    Keypath 说明 使用
    transform.scale 比例转化 @(cgfloat)
    transform.scale.x 宽的比例 @(cgfloat)
    transform.scale.y 高的比例 @(cgfloat)
    transform.rotation.x 围绕x轴旋转 @(M_PI)
    transform.rotation.y 围绕y轴旋转 @(M_PI)
    transform.rotation.z 围绕z轴旋转 @(M_PI)
    cornerRadius 圆角的设置 @(50)
    backgroundColor 背景颜色的变化 (id)[uicolor redcolor].cgcolor
    bounds 大小,中心不变 [nsvalue valuewithcgrect:]
    position 位置(中心点的改变) [nsvalue valuewithcgpoint]
    contents 内容(比如图片) (id)image.cgimage
    opacity 透明度 @(cgfloat)
    contentsRect.size.width 横向拉伸缩放(需要设置contents) @(cgfloat)

    CABasicAnimation的例子

    做一个从上往下走的动画

    CABasicAnimation *basicani = [CABasicAnimation animationWithKeyPath:@"position.y"];//中心点
    basicani.toValue = @400;
    basicani.duration = 2;
    //动画完成不移除状态
    basicani.removedOnCompletion = NO;
    //保持最后的状态
    basicani.fillMode = kCAFillModeForwards;
    basicani.delegate = self;
    [_RedView.layer addAnimation:basicani forKey:@""];
    

    CAKeyframeAnimation关键帧动画

    CAKeyframeAnimation设置动画的路径分为两种

    1. 设置path
    2. 设置一组的values

    CAKeyframeAnimation的例子

    代码如下:设置了一个飞机按照轨迹飞行的动画

    //贝塞尔曲线
    /*
     想要通过程序写曲线或者不规则的图形时,是很复杂的
     贝塞尔曲线就是通过代码转换成公式来进行绘制的。
     
     起点
     终点
     2个控制点
     **/
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(20, 200)];
    [path addCurveToPoint:CGPointMake(300, 200) controlPoint1:CGPointMake(100, 100) controlPoint2:CGPointMake(200, 300)];
        
    CALayer *planelayer = [CALayer layer];
    planelayer.backgroundColor = [UIColor redColor].CGColor;
    planelayer.frame = CGRectMake(0, 0, 40, 40);
    planelayer.anchorPoint = CGPointMake(0.5, 0.8);
    planelayer.contents = (id)[UIImage imageNamed:@"feiji"].CGImage;
    [self.view.layer addSublayer:planelayer];
        
    //需要添加到layer上
    //形状图 矢量图 这个事layer的子类,CAShapeLayer有一个硬件加速更快
    CAShapeLayer *shapelayer = [CAShapeLayer layer];
    shapelayer.path = path.CGPath;
    shapelayer.fillColor = nil;  //填充颜色
    shapelayer.strokeColor = [UIColor blueColor].CGColor;//画笔的颜色
    [self.view.layer addSublayer:shapelayer];
        
    /*
     CAKeyframeAnimation 设置动画的路径分为两种:
     1. 设置path
     2. 设置一组values
     ***/
        
    //飞行动画
    CAKeyframeAnimation *planeAni = [CAKeyframeAnimation animation];
    planeAni.keyPath = @"position";
    planeAni.path = path.CGPath;
    planeAni.rotationMode = kCAAnimationRotateAuto;
    planeAni.duration = 3.5;
    planeAni.fillMode = kCAFillModeForwards;
    planeAni.removedOnCompletion = NO;
    [planelayer addAnimation:planeAni forKey:@"planeAni"];
    

    CAAnimationGroup动画组

    将多个动画合并到一起的动画就是CAAnimationGroup动画组

    CAAnimationGroup例子

    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(20, 200)];
    [path addCurveToPoint:CGPointMake(300, 200) controlPoint1:CGPointMake(100, 100) controlPoint2:CGPointMake(200, 300)];
        
    CALayer *colorlayer = [CALayer layer];
    colorlayer.frame = CGRectMake(0, 0, 60, 60);
    colorlayer.position = CGPointMake(50, 200);
    colorlayer.backgroundColor = [UIColor yellowColor].CGColor;
    [self.view.layer addSublayer:colorlayer];
    
    //飞行动画
    CAKeyframeAnimation *planeAni = [CAKeyframeAnimation animation];
    planeAni.keyPath = @"position";
    planeAni.path = path.CGPath;
    planeAni.rotationMode = kCAAnimationRotateAuto;
        
    //改变大小
    CABasicAnimation *sizeanim = [CABasicAnimation animation];
    sizeanim.keyPath = @"transform.scale";
    sizeanim.toValue = @0.5;
    
    //改变颜色
    CGFloat redcolor = arc4random() % 255 / 255.0f;
    CGFloat greencolor = arc4random() % 255 / 255.0f;
    CGFloat bluecolor = arc4random() % 255 / 255.0f ;
    UIColor *color = [UIColor colorWithRed:redcolor green:greencolor blue:bluecolor alpha:1];
        
    CABasicAnimation *colorAnim = [CABasicAnimation animation];
    colorAnim.keyPath = @"backgroundColor";
    colorAnim.toValue = (id)color.CGColor;
        
    CAAnimationGroup *group = [CAAnimationGroup animation];
    group.animations = @[planeAni, sizeanim, colorAnim];
    group.duration = 4.0f;
    group.fillMode = kCAFillModeForwards;
    group.removedOnCompletion = NO;
    [colorlayer addAnimation:group forKey:nil];
    

    转场动画

    CATransition转场动画

    CATransition是CAAnimation的子类,用于过渡动画或转场动画。为视图层移入移除屏幕提供转场动画。

    简单的转场动画ImageView的切换:

    
    @property (weak, nonatomic) IBOutlet UIImageView *ImageView;
    @property (strong,nonatomic)NSArray *images;
    @property (assign,nonatomic)NSInteger index;
    
    
    - (void)Createtransition{
        
        if(_index == 3){
            _index = 0;
        }
        _index ++ ;
        NSString *imagename = _images[_index];
        _ImageView.image = [UIImage imageNamed:imagename];
        CATransition *anima = [CATransition animation];
        anima.type = @"cube";
        [_ImageView.layer addAnimation:anima forKey:@"CATransitionkey"];
        
        
    }
    _images = @[@"1.jpg",@"2.jpg",@"3.jpg",@"4.jpg"];
    _index = 0;
    
    [self Createtransition];
    
    

    转场动画的重要属性

    • type:系统自带转场动画的类型

      • 官方自定
      CA_EXTERN NSString * const kCATransitionFade;
      CA_EXTERN NSString * const kCATransitionMoveIn;
      CA_EXTERN NSString * const kCATransitionPush;
      CA_EXTERN NSString * const kCATransitionReveal;
      
      • 私有的type:
      NSString *const kCATransitionCube = @"cube"; 
      NSString *const kCATransitionSuckEffect = @"suckEffect"; 
      NSString *const kCATransitionOglFlip = @"oglFlip"; 
      NSString *const kCATransitionRippleEffect = @"rippleEffect"; 
      NSString *const kCATransitionPageCurl = @"pageCurl"; 
      NSString *const kCATransitionPageUnCurl = @"pageUnCurl"; 
      NSString *const kCATransitionCameraIrisHollowOpen = @"cameraIrisHollowOpen";
      NSString *const kCATransitionCameraIrisHollowClose = @"cameraIrisHollowClose";
      
    • subtype:动画类型的方向

    CA_EXTERN NSString * const kCATransitionFromRight;
    CA_EXTERN NSString * const kCATransitionFromLeft;
    CA_EXTERN NSString * const kCATransitionFromTop;
    CA_EXTERN NSString * const kCATransitionFromBottom;
    

    transitionFromViewController

    转场动画的type

    UIViewAnimationOptionTransitionNone            = 0 << 20, // default
    UIViewAnimationOptionTransitionFlipFromLeft    = 1 << 20,
    UIViewAnimationOptionTransitionFlipFromRight   = 2 << 20,
    UIViewAnimationOptionTransitionCurlUp          = 3 << 20,
    UIViewAnimationOptionTransitionCurlDown        = 4 << 20,
    UIViewAnimationOptionTransitionCrossDissolve   = 5 << 20,
    UIViewAnimationOptionTransitionFlipFromTop     = 6 << 20,
    UIViewAnimationOptionTransitionFlipFromBottom  = 7 << 20,
    
    AViewController *a = self.childViewControllers[0];
    BViewController *b = self.childViewControllers[1];
    CViewController *c = self.childViewControllers[2];
    
    [self transitionFromViewController:_currentViewController
                      toViewController:b
                              duration:0.5
                               options:UIViewAnimationOptionTransitionFlipFromRight
                            animations:^{
    
    } completion:^(BOOL finished) {
    
    }];
    

    Transition Animation(自定义转场动画)

    第一种:presented& Dismiss
    需要实现UIViewController中的UIViewControllerTransitioningDelegate协议,实现下面两个API:

    //presented会调用的方法
    - (nullable id <UIViewControllerAnimatedTransitioning>) v:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
    //Dismiss会调用的方法
    - (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
    

    第二种:Push&Pop

    需要实现navigationController中的UINavigationControllerDelegate协议,实现下面一个API:

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

    上述API的返回值id <UIViewControllerAnimatedTransitioning>就是我们需要自定义的动画

    • 自定义转场动画步骤:

      1. 创建一个NSObject的对象

      2. 核心:遵守<UIViewControllerAnimatedTransitioning>

      3. 实现下面2个方法:

        //动画持续时间,单位是秒
        - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
        }
        
        //动画效果
        - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
        }
        
      4. animateTransition方法中实现你的动画

      5. 动画结束后调用completeTransition告诉系统转场动画结束

    • 自定义转场动画的核心思想

      • containerView 动画的容器
      • ToVC
      • FromVC
      • mask
      • completeTransition动画完成

    Push&Pop形式转场动画

    • 自定义转场动画入口:

      1. 在Push和Pop的ViewController遵守UINavigationControllerDelegate协议

      2. 实现下面方法,并返回自定义动画:

        <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                                                    animationControllerForOperation:(UINavigationControllerOperation)operation
                                                                 fromViewController:(UIViewController *)fromVC
                                                                   toViewController:(UIViewController *)toVC{
            
            if(operation == UINavigationControllerOperationPush){
                //Push
                return 返回你的自定义动画
            }
            if(operation == UINavigationControllerOperationPop){
                //Pop
                return 返回你的自定义动画
            }
            return nil;
        }
        
    • 自定义转场动画实现代码:

    //Pop
    - (void)CreatePopAnimation:(id <UIViewControllerContextTransitioning>)transitionContext{
        
        UIViewController * fromVC   = [_context viewControllerForKey:UITransitionContextFromViewControllerKey];
        UIViewController *toVC      = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
        UIView *containerView       = [transitionContext containerView];
        [containerView addSubview:toVC.view];
        [containerView addSubview:fromVC.view];
    
        maskLayer.path              = SmallPath.CGPath;
        fromVC.view.layer.mask      = maskLayer;
        
        CABasicAnimation *anim      = [CABasicAnimation animation];
        anim.keyPath                = @"path";
        anim.fromValue              = (__bridge id _Nullable)(BigPath.CGPath);
        anim.toValue                = (__bridge id)(SmallPath.CGPath);
        anim.duration               = [self transitionDuration:_context];
        anim.timingFunction         = [CAMediaTimingFunction  functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
        anim.delegate               = self;
        [maskLayer addAnimation:anim forKey:nil];
        
    }
    
    //push
    - (void)CreatePushAnimation:(id <UIViewControllerContextTransitioning>)transitionContext{
        
        UIViewController *toVC      = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
        UIView *containerView       = [transitionContext containerView];
        [containerView addSubview:toVC.view];
    
        maskLayer.path              = BigPath.CGPath;
        toVC.view.layer.mask        = maskLayer;
    
        CABasicAnimation *anim      = [CABasicAnimation animationWithKeyPath:@"path"];
        anim.fromValue              = (__bridge id)(SmallPath.CGPath);
        anim.toValue                = (__bridge id)((BigPath.CGPath));
        anim.duration               = [self transitionDuration:_context];
        anim.timingFunction         = [CAMediaTimingFunction  functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
        anim.delegate               = self;
        [maskLayer addAnimation:anim forKey:nil];
    }
    

    Presentation&Dismiss形式转场动画

    • 自定义转场动画入口:

      1. present后的视图ToVC遵守UIViewControllerTransitioningDelegate协议
        • self.transitioningDelegate = self;
        • self.modalPresentationStyle = UIModalPresentationCustom;
      2. 实现下面方法,并返回自定义动画:
      - (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source{
      }
      
      - (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed{
      }
      
    • 自定义动画代码实现

    //dissmiss
    - (void)CreateDismissAnimation:(id <UIViewControllerContextTransitioning>)transitionContext{
        
        UIView* containerView   = [transitionContext containerView];
        TwoVC * fromVC          = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
         [containerView addSubview:fromVC.view];
        
        maskLayer.path              = SmallPath.CGPath;
        fromVC.view.layer.mask      = maskLayer;
    
        CABasicAnimation *anim      = [CABasicAnimation animation];
        anim.keyPath                = @"path";
        anim.fromValue              = (__bridge id _Nullable)(BigPath.CGPath);
        anim.toValue                = (__bridge id _Nullable)(SmallPath.CGPath);
        anim.duration               = [self transitionDuration:_context];
        anim.timingFunction         = [CAMediaTimingFunction  functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
        anim.delegate               = self;
        [maskLayer addAnimation:anim forKey:nil];
      
    }
    
    //Presentation
    - (void)CreatePresentationAnimation:(id <UIViewControllerContextTransitioning>)transitionContext{
        
        UIViewController *toVC      = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
        toVC.view.frame             = CGRectMake(0, 0, GGScreenwidth, GGScreenheight);
        UIView *containerView       = [transitionContext containerView];
        [containerView addSubview:toVC.view];
        
        maskLayer.path              = BigPath.CGPath;
        toVC.view.layer.mask        = maskLayer;
    
        CABasicAnimation *anim      = [CABasicAnimation animation];
        anim.keyPath                = @"path";
        anim.fromValue              = (__bridge id _Nullable)(SmallPath.CGPath);
        anim.toValue                = (__bridge id _Nullable)(BigPath.CGPath);
        anim.duration               = [self transitionDuration:_context];
        anim.timingFunction         = [CAMediaTimingFunction  functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
        anim.delegate               = self;
        [maskLayer addAnimation:anim forKey:nil];
    }
    

    转场动画代码 我已经集成了Push&Pop和Present&Dismiss,码字不容易,码代码更不容易,github顺手给个星星

    转场效果:

    相关文章

      网友评论

        本文标题:iOS基础全面分析之四(动画全面分析)

        本文链接:https://www.haomeiwen.com/subject/cuaxkctx.html