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

看着我这标题就觉得有点狂啊,其实是本人最近工作有点闲,看了很多人写的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;
          }
      }
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,367评论 6 512
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,959评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,750评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,226评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,252评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,975评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,592评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,497评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,027评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,147评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,274评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,953评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,623评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,143评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,260评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,607评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,271评论 2 358

推荐阅读更多精彩内容

  • 前言:关于贝塞尔曲线与CAShapeLayer的学习 学习Demo演示: 贝塞尔曲线简单了解 使用UIBezier...
    麦穗0615阅读 17,875评论 18 149
  • Core Animation Core Animation,中文翻译为核心动画,它是一组非常强大的动画处理API,...
    45b645c5912e阅读 3,032评论 0 21
  • 显式动画 显式动画,它能够对一些属性做指定的自定义动画,或者创建非线性动画,比如沿着任意一条曲线移动。 属性动画 ...
    清风沐沐阅读 1,941评论 1 5
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌。在这里你可以看...
    F麦子阅读 5,113评论 5 13
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥ios动画全貌。在这里你可以看...
    每天刷两次牙阅读 8,509评论 6 30