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

看着我这标题就觉得有点狂啊,其实是本人最近工作有点闲,看了很多人写的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);
    }
    这段代码之后呈现出的效果如下:
所有的动画、绘图、这里全搞定_第1张图片

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

// 画一个矩形
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;
    需要注意的是这两行要同时写上动画才不会回去。
    动画执行的效果如下:
所有的动画、绘图、这里全搞定_第2张图片

另外你可以利用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度,运行效果如下:


所有的动画、绘图、这里全搞定_第3张图片

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


所有的动画、绘图、这里全搞定_第4张图片

由于我们改变了描点,所以图像的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时,显示方法是内外各一半。为了便于观察,这里运行前把上面的描点的那句代码注释掉,运行效果如下:


所有的动画、绘图、这里全搞定_第5张图片
  • 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];
    }
    最后实现的效果如下:
所有的动画、绘图、这里全搞定_第6张图片
抖动动画
  • 动画但暂停
    如果动画时加在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;
          }
      }
    

你可能感兴趣的:(所有的动画、绘图、这里全搞定)