所有的动画、绘图、这里全搞定

作者: SlimMan | 来源:发表于2015-09-08 11:31 被阅读1681次

    看着我这标题就觉得有点狂啊,其实是本人最近工作有点闲,看了很多人写的sample,从中萃取精华,总结下,以防止自己忘记了,岂不白白浪费了这几天的努力。废话就这么多,现在开始正题:
    先说说咱这篇文章会讲到什么吧,首先我会讲讲绘图,然后讲讲动画
    咱们首先从绘图说起,

    • 使用CoreGraphics进行绘图
      说到绘图,那绘图是在哪里绘呢,当然是在View里面了,我们可以重写UIView的- (void)drawRect:(CGRect)rect方法,然后在里面进行绘图,绘图的话就离不来CoreGraphics了,然而CoreGraphics的核心就是QuzCore里面的几个函数了。废话少说,直接上代码
      - (void)drawRect:(CGRect)rect {
      // 获取绘图上下文环境
      CGContextRef context = UIGraphicsGetCurrentContext();
      // 设置线条的宽度
      CGContextSetLineWidth(context, 4.0);
      // 设置线条的颜色
      CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
      // 将绘画笔移到一个点作为绘图的起点
      CGContextMoveToPoint(context, 20.0, 20.0);
      // 由起点画一条线到终点
      CGContextAddLineToPoint(context, 300, 400);
      // 画出来
      CGContextStrokePath(context);
      }
      这段代码之后呈现出的效果如下:

    如你所见,这段代码的功能是画了一条红色的斜线。当然如果你想画一个矩形的话也是同样的一个道理,在上面的代码后面加上如下代码就好:

    // 画一个矩形
    CGRect arect = CGRectMake(200, 30, 49, 59);
    // 设置填充色
    CGContextSetFillColorWithColor(context, [UIColor blueColor].CGColor);
    // 填充矩形
    CGContextFillRect(context, arect);
    // 将举行显示出来
    CGContextAddRect(context, arect);
    

    效果的话自己去试吧,我就不上图了,另外谁能告诉我怎么把图片缩小,这太占地了。绘图就说这么多了,感觉用处不大啊,以后感觉用处大再补充吧。

    • 使用CAShapeLayer 、UIBezierPath、CABasicAnimation画
      经常看到别人发出一下比较屌的动画例子,其中用到的两个必不可少的技术点有三个,分别是:
      CAShapeLayer:这是动画的主要载体,动画都是由他执行
      UIBezierPath:用来描绘CAShapeLayer的边界
      CABasicAnimation:动画对象,由他对动画的执行过程进行描述
      使用实例如下:
      #import "NibView.h"
      @interface NibView()

        // 执行动画的载体
        @property (nonatomic, strong) CAShapeLayer *shapLayer;
      
        @end
      
        @implementation NibView
      
        /*
        // Only override drawRect: if you perform custom drawing.
        // An empty implementation adversely affects performance during animation.
        - (void)drawRect:(CGRect)rect {
            // Drawing code
        }
        */
      
        // 画面的初始化在此方法内进行
        - (id)initWithCoder:(NSCoder *)aDecoder {
            self = [super initWithCoder:aDecoder];
            self.frame = [UIApplication sharedApplication].keyWindow.bounds;
            self.backgroundColor = [UIColor redColor];
            
            UIBezierPath *bpath = [UIBezierPath bezierPath];
            [bpath moveToPoint:CGPointMake(100, 150)];
            [bpath addCurveToPoint:CGPointMake(300, 150)
                     controlPoint1:CGPointMake(200, 80)
                     controlPoint2:CGPointMake(200, 200)];
            [bpath addCurveToPoint:CGPointMake(300, 400)
                     controlPoint1:CGPointMake(400, 230)
                     controlPoint2:CGPointMake(250, 350)];
            [bpath addLineToPoint:CGPointMake(100, 400)];
            [bpath closePath];
            
            self.shapLayer.path = bpath.CGPath;
            
            [self.layer addSublayer:_shapLayer];
            
            return self;
        }
      
        // 获得一个path
        - (UIBezierPath *)rectPath {
            UIBezierPath *rectpath = [UIBezierPath bezierPath];
            [rectpath moveToPoint:CGPointMake(100.0, 150.0)];
            [rectpath addLineToPoint:CGPointMake(300, 150)];
            [rectpath addLineToPoint:CGPointMake(300, 400)];
            [rectpath addLineToPoint:CGPointMake(100, 400)];
            [rectpath closePath];
            return rectpath;
        }
      
        // 执行动画
        - (void)animate {
            CABasicAnimation *expandAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
            expandAnimation.fromValue = (__bridge id)(_shapLayer.path);
            expandAnimation.toValue = (__bridge id)[self rectPath].CGPath;
            expandAnimation.beginTime = 0;
            expandAnimation.duration = 0.5;
            expandAnimation.fillMode = kCAFillModeForwards;
            expandAnimation.removedOnCompletion = NO;
            [self.shapLayer addAnimation:expandAnimation forKey:nil];
            
        }
      
        // 加个按钮让动画可以重复进行
        - (IBAction)goAnimate:(UIButton *)sender {
            [self animate];
        }
      
        #pragma mark - initViews
        - (CAShapeLayer *)shapLayer {
            if (!_shapLayer) {
                _shapLayer = [[CAShapeLayer alloc] init];
                _shapLayer.frame = self.bounds;
                _shapLayer.fillColor = [UIColor greenColor].CGColor;
            }
            return _shapLayer;
        }
      
        @end
      

    下面对上面的代码进行说明:

    1. 我这里是将代码动画放在一个xib文件里面的,需要注意的是当xib文件被加载时它调用的是- (id)initWithCoder:(NSCoder *)aDecoder这个方法,而不是- (id)initWithFrame:(CGRect)frame这个方法,使用初始化的事情都应该放在这里面来进行。
    2. 在animate这个方法里面有这么两行
      expandAnimation.fillMode = kCAFillModeForwards;
      expandAnimation.removedOnCompletion = NO;
      需要注意的是这两行要同时写上动画才不会回去。
      动画执行的效果如下:

    另外你可以利用CAAnimationGroup来组织来管理动画,使得几个动画连续执行,这样可以形成一系列的连贯动画效果,例如:
    #import "NibView.h"

        @interface NibView()
    
        // 执行动画的对戏那个
        @property (nonatomic, strong) CAShapeLayer *shapLayer;
    
        @end
    
        @implementation NibView
    
        /*
        // Only override drawRect: if you perform custom drawing.
        // An empty implementation adversely affects performance during animation.
        - (void)drawRect:(CGRect)rect {
            // Drawing code
        }
        */
    
        // 画面的初始化在此方法内进行
        - (id)initWithCoder:(NSCoder *)aDecoder {
            self = [super initWithCoder:aDecoder];
            self.frame = [UIApplication sharedApplication].keyWindow.bounds;
            self.backgroundColor = [UIColor redColor];
            self.shapLayer.path = [self curvePath].CGPath;
            [self.layer addSublayer:_shapLayer];
            return self;
        }
    
        // 获得一个饱含曲线的path
        - (UIBezierPath *)curvePath {
            UIBezierPath *bpath = [UIBezierPath bezierPath];
            [bpath moveToPoint:CGPointMake(70, 150)];
            [bpath addCurveToPoint:CGPointMake(270, 150)
                     controlPoint1:CGPointMake(170, 80)
                     controlPoint2:CGPointMake(170, 200)];
            [bpath addCurveToPoint:CGPointMake(270, 400)
                     controlPoint1:CGPointMake(370, 230)
                     controlPoint2:CGPointMake(220, 350)];
            [bpath addLineToPoint:CGPointMake(70, 400)];
            [bpath closePath];
            return bpath;
        }
    
    
        // 获得一个path
        - (UIBezierPath *)rectPath {
            UIBezierPath *rectpath = [UIBezierPath bezierPath];
            [rectpath moveToPoint:CGPointMake(100.0, 150.0)];
            [rectpath addLineToPoint:CGPointMake(300, 150)];
            [rectpath addLineToPoint:CGPointMake(300, 400)];
            [rectpath addLineToPoint:CGPointMake(100, 400)];
            [rectpath closePath];
            return rectpath;
        }
    
        // 获得一个正方形边迹
        - (UIBezierPath *)suqarePath {
            UIBezierPath *squarPath = [UIBezierPath bezierPath];
            [squarPath moveToPoint:CGPointMake(10, 150)];
            [squarPath addLineToPoint:CGPointMake(310, 150)];
            [squarPath addLineToPoint:CGPointMake(310, 450)];
            [squarPath addLineToPoint:CGPointMake(10, 450)];
            [squarPath closePath];
            return squarPath;
        }
    
    
        // 执行动画
        - (void)animate {
            // 原始形态变成长方形
            CABasicAnimation *expandAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
            expandAnimation.fromValue = (__bridge id)(_shapLayer.path);
            expandAnimation.toValue = (__bridge id)[self rectPath].CGPath;
            expandAnimation.beginTime = 0;
            expandAnimation.duration = 0.5;
            [self.shapLayer addAnimation:expandAnimation forKey:nil];
            // 长方形变成正方形
            CABasicAnimation *expandAnimation2 = [CABasicAnimation animationWithKeyPath:@"path"];
            expandAnimation2.fromValue = (__bridge id)[self rectPath].CGPath;
            expandAnimation2.toValue = (__bridge id)[self suqarePath].CGPath;
            expandAnimation2.beginTime = expandAnimation.beginTime + expandAnimation.duration;
            expandAnimation2.duration = 0.5;
            // 正方形又回到原始形态
            CABasicAnimation *expandAnimation3 = [CABasicAnimation animationWithKeyPath:@"path"];
            expandAnimation3.fromValue = (__bridge id)[self suqarePath].CGPath;
            expandAnimation3.toValue = (__bridge id)(_shapLayer.path);
            expandAnimation3.beginTime = expandAnimation2.beginTime + expandAnimation2.duration;
            expandAnimation3.duration = 0.5;
            
            CAAnimationGroup *group = [CAAnimationGroup animation];
            group.animations = @[expandAnimation, expandAnimation2, expandAnimation3];
            group.beginTime = expandAnimation.beginTime;
            group.duration = expandAnimation3.beginTime + expandAnimation3.duration;
            group.fillMode = kCAFillModeForwards;
            group.removedOnCompletion = NO;
            group.repeatCount = 2;
            [self.shapLayer addAnimation:group forKey:nil];
            
        }
    
        // 加个按钮让动画可以重复进行
        - (IBAction)goAnimate:(UIButton *)sender {
            [self animate];
        }
    
        #pragma mark - initViews
        - (CAShapeLayer *)shapLayer {
            if (!_shapLayer) {
                _shapLayer = [[CAShapeLayer alloc] init];
                _shapLayer.frame = self.bounds;
                _shapLayer.fillColor = [UIColor greenColor].CGColor;
            }
            return _shapLayer;
        }
    
        @end
    

    由于修改较大,使用干脆又重现全部贴了出来了,下面做以下说明:

    1. 首先为了代码的整齐我把initWithCoder里面的画边界部分的代码抽了出来,单独形成了一个方法
    2. 我增加了一个返回正方形的轨迹方法suqarePath
    3. 我对animate方法进行了修改,引入了CAAnimationGroup,这里仍需要注意的是fillMode和removedOnCompletion这两个熟悉,现在把他应用到动画组上。
    通过动画组,我们可以形成各种细腻的动画效果,一切貌似变的明朗起来.
    
    • 绕着Z轴旋转的方法
      有一种动画叫做旋转,对于旋转来说如果像上面一样一个个慢慢组的话会写死你有木有。那旋转动画怎么玩呢,很简单,看到我们上面声明动画是这么玩的:
      [CABasicAnimation animationWithKeyPath:@"path"],
      然而,现在我们将这么玩:
      [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]
      他们两的区别就是KeyPath变了,看到这里,我们明白了,其实动画的种类就是由这个东西决定的。是@"path"说明这个动画是通过改变边界来形成动画,那很自然@"transform.rotation.z"就是绕着Z轴旋转形成的动画了。废话不多说,再次上代码:
      首先在类中加入如下方法
      // 旋转动画
      - (void)rotation {
      CABasicAnimation *rotaionAnimate = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
      rotaionAnimate.toValue = @(M_PI * 2.0);
      rotaionAnimate.duration = 0.5;
      rotaionAnimate.removedOnCompletion = YES;
      [self.shapLayer addAnimation:rotaionAnimate forKey:nil];

        }
      

    然后在修改goAnimate方法
    - (CAShapeLayer *)shapLayer {
    if (!_shapLayer) {
    _shapLayer = [[CAShapeLayer alloc] init];
    _shapLayer.frame = self.bounds;
    _shapLayer.fillColor = [UIColor greenColor].CGColor;
    }
    return _shapLayer;
    }

    这里我们让图层旋转360度,运行效果如下:



    另外需要说明的是可以制定图像绕着那一点旋转,我们只要指定它的描点,在rotation方法里面加入下面这句代码
    self.shapLayer.anchorPoint = CGPointMake(0.1, 0.1);
    效果如下:



    由于我们改变了描点,所以图像的fram变了。
    • 描边动画
      有关描边动画也是改变一下KeyPath,它的KeyPath是:@"strokeEnd"
      在代码中加入如下方法
      - (void)strokeBoard {
      self.shapLayer.strokeColor = [UIColor whiteColor].CGColor;
      self.shapLayer.lineWidth = 20.0;
      CABasicAnimation *strokeAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
      strokeAnimation.fromValue = @0.0;
      strokeAnimation.toValue = @1.0;
      strokeAnimation.duration = 1.0;
      [self.shapLayer addAnimation:strokeAnimation forKey:nil];
      }
      然后在goAnimate方法里面加入如下代码
      [NSTimer scheduledTimerWithTimeInterval:4.5
      target:self
      selector:@selector(strokeBoard)
      userInfo:nil
      repeats:NO];

    这里strokeBoard方法里面我们设置了shapLayer的lineWidth和strokeColor,lineWidth默认是0,strokeColor默认是透明的。
    当lineWidth非0时,显示方法是内外各一半。为了便于观察,这里运行前把上面的描点的那句代码注释掉,运行效果如下:


    • CAKeyframeAnimation:关键帧动画。(和CABasicAnimation平行)就是一帧一帧的动画了,一般用来处理GIF图片的
      首先可以在View里面加入一张图片,然后让图片抖动起来,抖动但动画代码如下:
      // 抖动动画
      - (void)animate {
      // 定义帧动画
      CAKeyframeAnimation animate = [CAKeyframeAnimation animation];
      // 改变弧度
      animate.values = @[@(M_PI/180
      5),@(-M_PI/1805),@(M_PI/1805)];
      // 关键帧是什么必须有,其实就是和什么有关的动画了
      animate.keyPath = @"transform.rotation";
      // 重复次数
      animate.repeatCount = MAXFLOAT;
      animate.duration = 0.2;
      [_imageView.layer addAnimation:animate forKey:nil];
      }
      最后实现的效果如下:
    抖动动画
    • 动画但暂停
      如果动画时加在layer上的话,动画是可以被暂停的,只需要将执行动画的那个layer的speed属性设置为0就好,要回复的话设置成1.0就好
      如暂停和恢复上面的动画:
      - (void)stopAnimate {

            // 获取暂停时间
        //    CFTimeInterval pausedTime = [self.imageView.layer convertTime:CACurrentMediaTime() fromLayer:nil];
        //    
        //    self.imageView.layer.timeOffset = pausedTime;
      
            if (self.imageView.layer.speed == 0) {
                self.imageView.layer.speed = 1.0;
            } else {
                self.imageView.layer.speed = 0;
            }
        }
      

    相关文章

      网友评论

      本文标题:所有的动画、绘图、这里全搞定

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