美文网首页
Quartz2D 第一集

Quartz2D 第一集

作者: 小的小碰撞 | 来源:发表于2017-09-19 13:49 被阅读0次

    01-PPT介绍

    1.什么是Quartz2D?
        他是一个二维的绘图引擎,同时支持iOS和Mac系统
        
    2.Quartz2D能完成的工作
        画基本线条,绘制文字,图片,截图,自定义UIView.
        
    3.Quartz2D实例演示.
    
    4.Quartz2D在开发中的价值
        当我们的控件样式极其复杂时,可以把控件内部的结构给画出画,就是自定义控件.
        
    5.什么是图形上下文,上下文的类型有哪些?
        图形上下文是用来保存用户绘制的内容状态,并决定绘制到哪个地方的.
        用户把绘制好的内容先保存到图形上下文,
        然后根据选择的图形上下文的不同,绘制的内容显示到地方也不相同,即输出目标也不相同.
        
        图形上下文的类型有:
            Bitmap Graphics Context(位图上下文)
            PDF Graphics Context
            Window Graphics Context 
            Layer Graphics Context(图层上下文,自定义UIView取得上下文就是图层上下文.
            UIView之所以能够显示就是因为他内部有一个图层)
            Printer Graphics Context
            
    6.如何自定义UIView,步骤是什么?
        
        首先得要有上下文,有了上下文才能决定把绘制的东西显示到哪个地方去.
        其次就是这个上下文必须得和View相关联.才能将内容绘制到View上面.
        
        步骤:
        1.要先自定定UIView
        2.实现DrawRect方法
        3.在DrawRect方法中取得跟View相关联的上下文.
        4.绘制路径(描述路径长什么样).
        5.把描述好的路径保存到上下文(即:添加路径到上下文)
        6.把上下文的内容渲染到View
    

    02-基本线条绘制

    20170914220835.png 56D906CB7141820F64FD5CAF64CFAA90.jpg FD801E8882A4E3A2B1657BA2C024467F.jpg 24593C0E69289192A7614EE8FD485044.jpg 7BD9AE480CCDFCE784F4E1ED6DC5A1DE.jpg
    1.DrawRect方法作用?什么时候调用.
        DrawRect作用:专用在这个方法当中绘图的.只有在这个方法当中才能取得跟View相关联的上下文.
        DrawRect是系统自己调用的, 它是当View显示的时候自动调用.
    
    2.画线(基本步骤描述)
        2.1获取跟View相关联的上下文
         CGContextRef ctx = UIGraphicsGetCurrentContext();
         
        2.2绘制路径
        UIBezierPath *path = [UIBezierPath bezierPath];
        
        2.2.1设置起点
        [path moveToPoint:CGPointMake(10, 125)];
        
        2.2.2添加一根线到某个点
        [path addLineToPoint:CGPointMake(200, 125)];
        
        2.3把路径添加到上下文
         CGContextAddPath(ctx,path.CGPath);
         
        2.4把上下文的内容渲染到View上面.    
         CGContextStrokePath(ctx);
        
     3. 想要再添加一根线怎么办?
        第一种方法:重新设置起点,添加一根线到某个点,一个UIBezierPath路径上面可以有多条线.
        第二种方法:直接在原来的基础上添加线.把上一条的终点当做下一条线的起点.添加一根线到某个点
                  直接在下面addLineToPoint:
     
     4.怎么样设置线的宽度,颜色,样式?
        设置这些样式,我们称为是修改图形上下文的状态.
        设置线宽:CGContextSetLineWidth(ctx, 20);
        设置线段的连接样式: CGContextSetLineJoin(ctx, kCGLineJoinRound);
        添加顶角样式:CGContextSetLineCap(ctx, kCGLineCapRound);
        设置线的颜色: [[UIColor redColor] setStroke];
        
     5.如何画曲线?
        
        画曲线方法比较特殊需要一个控制点来决定曲线的弯曲程度.画曲线方法为:
        先设置一个曲线的起点
        [path moveToPoint:CGPointMake(10, 125)];
        再添加到个点到曲线的终点.同时还须要一个controlPoint(控件点决定曲线弯曲的方法程序)
        [path addQuadCurveToPoint:CGPointMake(240, 125) controlPoint:CGPointMake(125, 10)];
        
     6.如何画矩形,圆角矩形?
        
        画矩形直接利用UIBezierPath给我们封装好的路径方法
        (x,y)点决定了矩形左上角的点在哪个位置
        (width,height)是矩形的宽度高度
        bezierPathWithOvalInRect:CGRectMake(x, y, width, height)
        oval(椭圆)
        圆角矩形的画法多了一个参数,cornerRadius
        cornerRadius它是矩形的圆角半径.
        通过圆角矩形可以画一个圆.当矩形是正方形的时候,把圆角半径设为宽度的一半,就是一个圆.
        bezierPathWithRoundedRect:CGRectMake(10, 100, 50, 50) cornerRadius:10
        
     7.如果画椭圆,圆?
        
        画椭圆的方法为:
        前两个参数分别代码圆的圆心,后面两个参数分别代表圆的宽度,与高度.
        宽高都相等时,画的是一个正圆, 不相等时画的是一个椭圆
        bezierPathWithOvalInRect:CGRectMake(10, 100, 50, 50)
        
     8.如何利用UIKit封装的上下文进行画图?
        直接来个:[path stroke]就可以了.
        它底层的实现,就是获取上下文,拼接路径,把路径添加到上下文,渲染到View
        
     9.如何画圆弧?
        
         首先要确定圆才能确定圆弧,圆孤它就圆上的一个角度嘛
         
         Center:圆心
         radius:圆的半径
         startAngle:起始角度
         endAngle:终点角度
         clockwise:Yes顺时针,No逆时针
     
         注意:startAngle角度的位置是从圆的最右侧为0度.
         
         UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(125, 125)
                                                              radius:100 
                                                              startAngle:0 
                                                              endAngle:M_PI * 2 
                                                              clockwise:YES];
         
         
     10.如果画扇形.
        画扇形的方法为:先画一个圆孤再添加一个一根线到圆心,然后关闭路径.
                     关闭路径就会自动从路径的终点到路径的起点封闭起下
        用填充的话,它会默认做一个封闭路径,从路径的终点到起点. 
        关闭路径,
        [path closePath];
        [path fill];
    

    03-下载进条.

    1.搭建界面.
    
    2.拖动滑竿的时候让他里面的能够跟着我的拖动,数字在改变.
      数字改变时有一个注意点, 就是要显示%,它是一个特殊的符号,要用两个%%代表一个%
      
    3.拖动滑竿的时候就是在上面画弧.
     从最上面,按顺时针画,所以,它的起始角度是-90度.结束角度也是-90度
     也是从起始角度开始画,
     起始角度-90度, 看你下载进度是多少
     假如说你下载进度是100,就是1 * 360度
     也就是说这个进度占你360度多少分之一
    
     CGContextRef ctx = UIGraphicsGetCurrentContext();
     CGPoint center = CGPointMake(50, 50);
     CGFloat radius = rect.size.width * 0.5;
     CGFloat startA = -M_PI_2;
     CGFloat endA = -M_PI_2 + M_PI * 2 * progress;
     
     UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center 
                                                          radius:radius 
                                                          startAngle:startA 
                                                          endAngle:endA 
                                                          clockwise:YES];
     
    要获得Progress的值,这个进度值没有, 所以要传进来才能画.弄一个成员变量
    要在值改变的时候就要传进来.
    要拿到ProgressView才能够传进来,所以要拖线,拿到ProgressView
    所有都做好的, 发现没有画圆孤?
    为什么?
    问题:drawRect方法总共调用多少次?
         总共就调用一次, 第一次Progress为0,以后都不会执行了
    解决:每次传的时候,就要画一次,
        重写Progress方法
     -(void)setProgress:(CGFloat)progress{
        _progress = progress;
        手动调用drawRect方法, 让它重新绘制
        [self setNeedsDisplay];
     }
     运行发现还是不画,为什么?
     原因:drawRect方法是不能手动调用,因为在drawRect方法中才能获取跟View相关联的上下文
         系统在调用DrawRect方法时,会自动帮你创建一个跟View相关联的上下文,并且传递给它.
        自己调用的,没有给drawRect方法传递上下文.所以在draw方法中拿不到上下文.
    
     解决办法:想要重绘,调用[self setNeedsDisplay];
            告诉系统重新绘制View,系统就会自动帮你调用drawRect方法,系统在调用
            drawRect方法,它会帮你创建上下文
    

    04-画饼图

        第一步, 获取上下文
        第二步,拼接路径 ,绘制第一个扇形
         获取上下文
         CGContextRef ctx =  UIGraphicsGetCurrentContext();
         CGPoint center = CGPointMake(125, 125);
         CGFloat radius = 100;
         CGFloat startA = 0;
         CGFloat endA = 0;
         CGFloat angle = 25 / 100.0 * M_PI * 2;
         endA = startA + angle;
         拼接路径
         UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center 
                                                              radius:radius 
                                                              startAngle:startA 
                                                              endAngle:endA 
                                                              clockwise:YES];
         添加一根线到圆心
         [path addLineToPoint:center];
         把路径添加到上下文
         CGContextAddPath(ctx, path.CGPath);
         把上下文渲染到View
         CGContextFillPath(ctx);
        
         注意点: CGFloat angle = 25 / 100.0 * M_PI * 2; 
                100.0一定要加一个.0
    
        绘制第二个扇形
        一样的, 把路径描述第二个扇形就好了
        直接来个path =
        让Path指针重新指向一个新的对象.也就是把指针重复利用了
        之前的那个对象已经用完了,已经添加到了上下文当中了.
    
         第二个扇形
         startA = endA;
         angle = 25 / 100.0 * M_PI * 2;
         endA = startA + angle;
         path = [UIBezierPath bezierPathWithArcCenter:center
                                                 radius:radius 
                                                 startAngle:startA 
                                                 endAngle:endA 
                                                 clockwise:YES];
         [path addLineToPoint:center];
         把二个路径添加到上下文
         CGContextAddPath(ctx, path.CGPath);
         把上下文渲染到View
         CGContextFillPath(ctx);
    
    
        添加第二个扇形之后, 发现它们的颜色都一样,想要修改它的颜色
        在下面再写一个
        [[UIColor greenColor] set];
        下面的一个颜色把之前的东西给覆盖了.
        解决办法, 让它渲染两次
    
        第三个扇形,把第二个拷贝一下就好了
    
    
        总结:
            有没有发现在画三个扇形用太多代码了,
            里面有很多代码相似.
            是不是可以把代码给抽一下
            可以用便利数组的的方式
            发现就两个地方变了, 一个数字变了, 一个颜色变了.
    
    
        抽取代码:
            假设他给一组数据
            NSArray datas =  @[@25,@25,@50];
            把数组便利出来
            
    
         NSArray *datas =  @[@25,@25,@50];
         
         CGPoint center = CGPointMake(125, 125);
         CGFloat radius = 100;
         CGFloat startA = 0;
         CGFloat angle = 0;
         CGFloat endA = 0;
         
         for (NSNumber *number in datas) {
         
         startA = endA;
         angle = number.intValue / 100.0 * M_PI * 2;
         endA = startA + angle;
         
         描述路径
         UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center 
                                              radius:radius
                                              startAngle:startA
                                              endAngle:endA
                                              clockwise:YES];
         
         [path addLineToPoint:center];
         [[self randomColor] set];
         [path fill];
         
         }
    
         - (UIColor *)randomColor{
         CGFloat r = arc4random_uniform(256)/ 255.0;
         CGFloat g = arc4random_uniform(256)/ 255.0;
         CGFloat b = arc4random_uniform(256)/ 255.0;
         return [UIColor colorWithRed:r green:g blue:b alpha:1];
         
         }
            
        随机颜色:alpha通道它的取值范围是0-255;
                OC里面的取值范围只能是0到1,把它 / 255.0就让它到这个范围了,
                arc4random_uniform(256)随机产生 0 - 255的数.
                颜色通道它的取值范围是0 到 255.
                所以说要把0 到 255转换成0 到 1
                直接是  0 ~ 255 / 255.0就可以了.
                刚好是255就是255 / 255.0 就是1,
                刚才是0 就是 0 / 255.0 就是0.
    

    05-UIKit绘图演练

    一般使用UIKit给我们提供的绘图来绘制一些文字,图片这些东西.
    UIKit给我们提供画图的方法底层也是分为四步.所以也必须在drawRect方法当中去写.
    1.如何画文字?
        先创建好要画的文字
        使用UIKit提供的方法进行绘制.
        方法说明:
        drawAtPoint:要画到哪个位置
        withAttributes:文本的样式.
        [str drawAtPoint:CGPointZero withAttributes:nil];
        
    2.如何添加绘制文字属性?
        通过绘制方法的最后一个属性withAttributes来设置文字属性.
        它要求传入的是一个字典.它是通过字典的key和Value的形式来设置文字样式. 
        那传什么key,什么值我们可以在UIKit头文件当中的NSAttributedString类当中去找.
        使用形式如下:
        
         创建一个可变的字典,设置key,value
         NSMutableDictionary *dict = [NSMutableDictionary dictionary];
         字体
         dict[NSFontAttributeName] = [UIFont systemFontOfSize:50];
         颜色
         dict[NSForegroundColorAttributeName] = [UIColor redColor];
         设置边框颜色
         dict[NSStrokeColorAttributeName] = [UIColor redColor];
         dict[NSStrokeWidthAttributeName] = @1;
         阴影
         NSShadow *shadow = [[NSShadow alloc] init];
         shadow.shadowOffset = CGSizeMake(10, 10);
         shadow.shadowColor = [UIColor greenColor];
         shadow.shadowBlurRadius = 3;
         dict[NSShadowAttributeName] = shadow;
    
     3.drawAtPoint:和drawInRect:的区别?
         drawAtPoint:不能够自动换行
         drawInRect:能够自动换行
         
     4.如果绘制图片?
        绘制图片同样开始要先把图片素材导入.
        AtPoint:参数说明图片要绘制到哪个位置.
        通过调用UIKit的方法drawAtPoint:CGPointZero方法进行绘制;
        
     5.在绘制图片过程当中.drawAtPoint:和drawInRect:两个方法的区别?
        drawAtPoint:绘制出来的图图片跟图片的实际尺寸一样大
        drawInRect:使用这个方法绘制出来的图片尺寸会和传入的rect区域一样大.
     
     6.如果进行平铺图片?
        [image drawAsPatternInRect:rect];
     
     7.如何选用UIKit提供的方法快速画一个矩形?
        快速的用矩形去填充一个区域
        UIRectFill(rect);
        
     8.如何利用UIKit裁剪一个区域?
       UIRectClip(CGRectMake(0, 0, 50, 50));
       这个方法必须要设置好裁剪区域,才能有裁剪
    

    06-模仿系统的UIImageView

    整体思路:
    我们想要模仿系统的UIImageView,我们必须得要知道系统的UIView怎么用.
    系统的用法是创建一个UIImageView对象,设置frame,给它传递一个UIImage,再把它添加到一个View上面就可以了.
    可以切换图片.
    这是第一个用法.
    第二种用法,就是在创建的时候直接传递一个UIImage对象,使用initWithImage的方法进行创建一个UImageView的方式
    用这种做法创建出来的UIImageView它的尺寸大小和原始图片的尺寸大小一样大.
    所以我们自己的UIImageView也要具有这些功能.
    
    实现步骤:
    第一步:新建一个UIView,起名XMGImageView.
    第二步:给XMGImageView添加一个UIImage属性,供外界传递图片
    第三步:在DrawRect方法当中把传递的图片绘制到View上面
          绘制方法为:[_image drawInRect:rect],绘制的图片尺寸大小和UIView的尺寸大小一样大.
    第四步:重写UIImage属性的set方法,在set方法当中让View重新绘制.目的为了能够办到切换图片.
    第五步:提供一个- (instancetype)initWithImage:(UIImage *)image方法.
          在这个方法当中重写init方法
          在初始化时,让View尺寸和图片的实际大小一样大.
          然后再给UIImage属性赋值.
          这样在绘制图片的时候,显示出来的View已经有尺寸了, 尺寸大小和图片的实际大小一样大.
          
            具体代码实现:
            - (instancetype)initWithImage:(UIImage *)image{
            if (self = [super init]) {
                self.frame = CGRectMake(0, 0, image.size.width, image.size.height);
                _image = image;
            }
                return self;
            }
    
    
           -(void)setImage:(UIImage *)image{
              _image = image;
             [self setNeedsDisplay];
           }
          
           - (void)drawRect:(CGRect)rect {
                [_image drawInRect:rect];
           }
    

    07-定时器,雪花

    8508AB2354251B07D89F7074327D28F9.jpg 98A13CD01955D4FB35E1A6B927AF6B11.jpg
    1.定时器雪花整体思路:
        先在控制器View面绘制一个雪花.
        在View加载完毕后,添加一个定时器.
        在定时器方法当中调用得绘方法.
        在绘图方法当不段的去修改雪花的Y值.
        当雪花的Y值超过屏幕的高度时,让雪花的Y值重新设为0.从最顶部开始.
        
    2.添加定时器实现方案
        第一种采用NSTime
        第二种采用CADisplayLink
        最终采用CADisplayLink方案.
        
        2.1为什么采用CADisplayLink方案不用NSTime?
        
           首先要了解setNeedsDisplay
           setNeedsDisplay底层会调用DrawRect方法重绘.
           但是它不是立马就进行重绘.它仅仅是设置了一个重绘标志,等到下一次屏幕刷新的时候才会调用DrawRect方法.
           
           如果使用NSTime的话,假设是0.01调用一次重绘.假设屏幕0.02秒的时候它才刷新一次.中间就会等0.01秒.
           也就是每次都会等0.01秒这样累加上去.让变的越来越卡顿.
           
           使用CADisplayLink时,它的定时器方法就是屏幕每次刷新的时候就会调用(通常屏幕一秒钟刷新60次)
           它和setNeedsDisplay调用DrawRect方法的时机正好吻合,不会出间等待间隔.不会出现屏幕卡顿现象.
           
        2.2如何使用CADisplayLink添加定时器?
            Target:哪个对象要监听方法.
            selector:监听的方法名称.
            CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self
                                    selector:@selector(setNeedsDisplay)];
            想要让CADisplayLink工作,必须得要把它添加到主运行循环.
            只要添加到主运行循环, 跟模式没有关系
            [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
      
      3.具体实现代码如下:
      
             -(void)awakeFromNib{
                CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self
                                        selector:@selector(setNeedsDisplay)];
                [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
            
              }
              
                          
             - (void)drawRect:(CGRect)rect {
                if(_snowY > rect.size.height){
                    _snowY = 0;
                }
                UIImage *image = [UIImage imageNamed:@"雪花"];
                [image drawAtPoint:CGPointMake(0, _snowY)];
                _snowY += 10;
             }
    

    08-图形上下文状态栈

    上下文状态栈为内存中的一块区域,它用来保存前上下文当的状态.
    我们获取的图层上下文当中其实两块区域,一个是存放添加的路径,一个是用来保存用户设置的状态,
    这些状态包括线条的颜色,线宽等.
    当我们把上下文的内容渲染到View上面的时候, 
    它会自动将设置的所有上下文状态运行到保存的路径上面显示到View上面.
    
    如果想要有多种状态,可以先把路径渲染到View上面,
    再从新添加路径.添加完路径之后,重新设置上下文的状态.
    再把新设置的上下文状态渲染到View上面.
    
    我们可以利用上下文状态栈的方式,在设置状态之前,把之前的状态保存到上下文状态栈里面.
    下一次想要再使用之前的状态时, 可以从上下文状态当中取出之前保存的上下文状态.
    
    1.如何把上下文状态保存到上下文状态栈?
       CGContextSaveGState(ctx);
    2.如何从上下文状态栈中取出上下文状态?
       CGContextRestoreGState(ctx);
    

    09-上下文的矩阵操作

     上下文的矩阵操作其实就是修改上下文的形变,
      主要有以下几种
      平移
      CGContextTranslateCTM(ctx, 100, 100);
      旋转
      CGContextRotateCTM(ctx, M_2_PI);
      缩放
      CGContextScaleCTM(ctx, 0.5, 0.5);
      注意:上下文操作必须得要在添加路径之前去设置
    

    10.给图片添加水印.

    给图片添加水印.jpg
     给图片水印的目的:
     告诉别人图片的来源.
     防止别人盗用图片.打广告.
     
     添加水印它最终是生成了一个新的图片.
     生成图片要用到了图片上下文.不需要再去自定义View,
     之前一直在自定义View,是因为要拿跟View相关联的上下文.
     跟View相关联的上下文是系统自动帮我们创建的,所以不需要我们自己手动创建,
     但是图片上下文需要我们自己去手动创建.还需要我们自己手动去关闭.
     
     实现水印效果的思路:
     开启一个和原始图片一样的图片上下文.
     把原始图片先绘制到图片上下文.
     再把要添加的水印(文字,logo)等绘制到图片上下文.
     最后从上下文中取出一张图片.
     关闭图片上下文.
    
     1.如何开启一个图片上下文?
         size:开启多大的上文
         opaque:不透明度
         scale:缩放上下文.
         UIGraphicsBeginImageContextWithOptions(image.size, YES, 0);
         
     2.如何从图片上下文当中生成一张图片?
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
     
     3.如何关闭上下文?
        UIGraphicsEndImageContext();
    

    11-简单圆形图片裁剪

    简单圆形图片裁剪.jpg
    裁剪图片思路.
    开启一个图片上下文.
    上下文的大小和原始图片保持一样.以免图片被拉伸缩放.
    在上下文的上面添加一个圆形裁剪区域.圆形裁剪区域的半径大小和图片的宽度一样大.
    把要裁剪的图片绘制到图片上下文当中.
    从上下文当中取出图片.
    关闭上下文.
    
    1.如何设置圆形路径?
         UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:
                            CGRectMake(0, 0, image.size.width, image.size.width)];
    
                            
    2.如何把一个路径设为裁剪区域?
        [path addClip];

    相关文章

      网友评论

          本文标题:Quartz2D 第一集

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