• iOS学习——Quartz2D学习之UIKit绘制


    iOS学习——Quartz2D学习之UIKit绘制 

    1、总述

      在IOS中绘图技术主要包括:UIKit、Quartz 2D、Core Animation和OpenGL ES。其中Core Animation提供动画实现技术,OpenGL ES是OpenGL针对嵌入式设备的简化版本,用以绘制高性能的2D和3D图形。这里主要UIKit和Quartz 2D。

    • UIKit。它是高级别的图形接口,它的API都是基于Objective-C的。它能够访问绘图、动画、字体、图片等内容。
    • Quartz 2D。是IOS和Mac OS X环境下的2D绘图引擎。涉及内容包括:基于路径的绘图,透明度绘图,遮盖,阴影,透明层,颜色管理,防锯齿渲染,生成PDF,以及PDF元数据相关处理。Quartz 2D也被称为Core Graphics,缩写前缀为CG。Quartz 2D与Quartz Compositor统称为Quartz,Quartz原本是Mac OS X的Darwin核心之上的绘图技术。它的API接口都是基于C的。

      在IOS上无论采用哪种绘图技术(UIKit、Quartz 2D、Core Animation和OpenGL ES),都离不开UIView,绘制都发生在UIView对象的区域内。在绘制发生的时候如果使用的是系统提供的视图,绘制工作会自动得到处理。然而,如果是自定义视图,则必须重写drawRect:方法,在此提供相应的绘制代码。一旦drawRect:方法被调用,就可以使用任何的UIKit、Quartz 2D、OpenGL ES等技术对视图的内容进行绘制了

      绘图过程中除了使用了drawRect:方法,还有setNeedsDisplay和setNeedsDisplayInRect:。setNeedsDisplay和setNeedsDisplayInRect:方法是设置视图或者视图部分区域是否需要重新绘制,setNeedsDisplay是重新绘制整个视图,setNeedsDisplayInRect是重新绘制视图的部分区域。原则上,尽量不要绘制视图的全部,以减少绘制带来开销。触发视图重新绘制的动作有如下几种:

    • 当遮挡你的视图的其他视图被移动或删除操作的时候;
    • 将视图的hidden属性声明设置为NO,使其从隐藏状态变为可见;
    • 将视图滚出屏幕,然后再重新回到屏幕上;
    • 显式调用视图的setNeedsDisplay或者setNeedsDisplayInRect:方法

    2、UIKit的基本绘图功能

    UIKit提供非常基本的绘图功能,主要的API有: 
    - UIRectFill(CGRect rect),填充矩形函数 
    - UIRectFrame(CGRect rect),矩形描边函数 
    - UIBezierPath,绘制常见路径类,包括险段、渐变、阴影、反锯齿等高级特性支持还是不及Quartz 2D。

    3、文本绘制

    • 先创建好要画的文字 
    • 使用UIKit提供的方法进行绘制drawAtPoint:要画到哪个位置  withAttributes:文本的样式. 

    [str drawAtPoint:CGPointZero withAttributes:nil];

    • 前面说了,所有的绘制工作都应该放到drawRect:中进行,所以上面两步的代码不应该放在init、initWithFrame:或者AwakeFromNib方法中,在这些方法中这样写是画不出文字的。因为想要把一个东西画到View上面,必须获得该view的上下文 ,上下文只有在DrawRect方法中才能拿到

    4、drawAtPoint: withAttributes:的底层实现是怎样的?

    drawAtPoint: withAttributes:底层也是同样也是按以下着步骤来的:

      1. 第一步:获取上下文
      2. 第二步:拼接路径
      3. 第三步:把路径添加到上下文
      4. 第四步:渲染上下文到View

        所以,不管有没有上下文,只要在View上面画东西,都得要在DrawRect方法中去写

    5、如何添加绘制文字属性?

      通过绘制方法的最后一个属性withAttributes来设置文字属性它要求传入的是一个字典.它是通过字典的key和Value的形式来设置文字样式.那传什么key什么值我们可以在UIKit头文件当中的NSAttributedString类当中去找使用形式如下:

    - (void)drawRect:(CGRect)rect {
        
        NSString *str = @"打印信息 打印信息";
        
        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;
        //Attributes:给文字添加一些属性,富文本
        [str drawAtPoint:CGPointZero withAttributes:dict];
    } 

    6、drawAtPoint:和drawInRect:的区别? 

    •   drawAtPoint:不能够自动换行
    •   drawInRect:能够自动换行

    7、如何利用UIKit绘制一张图片?

      在前面我们学会了如何在自定义view中绘制文本信息,其实绘制图片的方法绘制文本的方法非常类似,所以基本步骤如下:

    1. 导入素材
    2. DrawRect加载图片  UIImage *image = [UIImage imageNamed:@"image001"];
    3. 绘制图片: [image drawAtPoint:CGPointZero];

    8、绘制图片有哪些方法,区别是什么?

      绘制图片的方法有三种,其区别分别如下:

    • drawAtPoint:(CGPoint *)point:从指定的点为图片的左上角的起点开始绘制,绘制出来的图形跟图片尺寸一样大,图片是按照原始大小进行绘制,吐过图片的大小超出当前view的视图范围,则无法进行绘制。
    • drawInRect:(CGRect *)rect:在指定的rect区域内绘制整张图片,图片会按照指定区域的宽高进行缩放,所以这种方式一定可以显示完整的图片,但是会进行一些缩放。
    • drawAsPatternInRect:(CGRect *)rect:在指定的rect区域内平铺图片,如果一张图片不够用,则会在剩下的地方重新放置该图片,图片的大小尺寸不会改变。

    9、如何选用UIKit提供的方法快速画一个矩形?

    • UIRectFill(rect);快速的用矩形去填充一个区域 

    • UIRectFrame(rect);快速绘制一个矩形的边框

     10、用UIKit裁剪一个区域

    • UIRectClip(CGRectMake(0, 0, 50, 50));只要超出裁剪区域部分,都会被裁剪掉
    • 这个方法必须要设置好裁剪区域,才能有裁剪
    • 把它放到最后面,没有裁剪效果
    //会填充整个rect的区域,指定的裁剪不会有效
    UIRectClip(CGRectMake(0, 0, 50, 50));
    UIRectFill(rect);
    
    //只会填充指定的裁剪区域,其他部分不会填充
    UIRectFill(rect);
    UIRectClip(CGRectMake(0, 0, 50, 50));

     11、图形上下文状态栈?

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

     12、如何保存到上下文状态栈,又如何从上下文状态栈中取出状态?

    1. 如何把上下文状态保存到上下文状态栈?  CGContextSaveGState(ctx);
    2. 如何从上下文状态栈中取出上下文状态? CGContextRestoreGState(ctx);

    13、上下文的矩阵操作

      上下文的矩阵操作其实就是修改上下文的形变主要有以下几种

    •   平移  CGContextTranslateCTM(ctx, 100, 100);
    •   旋转  CGContextRotateCTM(ctx, M_2_PI);
    •   缩放  CGContextScaleCTM(ctx, 0.5, 0.5);
    •   注意:上下文操作必须得要在添加路径之前去设置
    - (void)drawRect:(CGRect)rect {
        //1.获得跟View相关联的上下文
        CGContextRef ctx =  UIGraphicsGetCurrentContext();
        //2.描述路径
        UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-100, -50, 200, 100)];
        [[UIColor redColor] set];
        //上下文的矩阵操作,必须得要在添加路径之前做操作.
        //平移
        CGContextTranslateCTM(ctx, 100, 50);
        //缩放
        CGContextScaleCTM(ctx, 0.5, 0.5);   
        //旋转
        CGContextRotateCTM(ctx, M_PI_4);   
        //3.把路径 添加到当前上下文
        CGContextAddPath(ctx, path.CGPath);  
        //4.把上下文的内容渲染出来.
        CGContextFillPath(ctx);
        
    }

    14、如何给图片加水印?

      添加水印它最终是生成了一个新的图片。之前一直在自定义View是因为要拿跟View相关联的上下文跟View相关联的上下文是系统自动帮我们创建的所以不需要我们自己手动创建但是生成图片要用到图片上下文,图片上下文需要我们自己去手动创建还需要我们自己手动去关闭,所以不需要再去自定义View

    实现水印效果的思路:

    • 开启一个和原始图片一样的图片上下文.
    • 把原始图片先绘制到图片上下文.
    • 再把要添加的水印(文字,logo)等绘制到图片上下文.
    • 最后从上下文中取出一张图片.
    • 关闭图片上下文.

    1.如何开启一个图片上下文? 

        UIGraphicsBeginImageContextWithOptions(image.size, YES, 0);

        size:开启多大的上文

        opaque:不透明度

        scale:缩放上下文.

    2.如何从图片上下文当中生成一张图片? 

        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();

    3.如何关闭上下文?  

        UIGraphicsEndImageContext();

    - (void)viewDidLoad {
        [super viewDidLoad];
        //生成图片.
        UIImage *image = [UIImage imageNamed:@"image001"];
        //size:开开启一个多大图片上下文.
        //opaque:不透度
        //scale:0
        //开启跟图片相同的大小上下文.
        UIGraphicsBeginImageContextWithOptions(image.size, NO, 0.0);
        //把图片给绘制图片上下文.
        [image drawAtPoint:CGPointZero];
        //绘制水印 文字或图片都 ok
        NSString *str = @"小码哥";
        [str drawAtPoint:CGPointZero withAttributes:@{NSFontAttributeName : [UIFont systemFontOfSize:20]}];
        //生成图片
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
        //手动关闭上下文
        UIGraphicsEndImageContext();
        //显示添加水印后的新图片
        self.imageV.image = newImage;
    }

    15、如何进行图片裁剪?

    对图片进行裁剪的流程如下: 先添加裁剪去,再进行绘制

    • 开启一个图片上下文.
    • 上下文的大小和原始图片保持一样,以免图片被拉伸缩放.
    • 在上下文的上面添加一个圆形裁剪区域,圆形裁剪区域的半径大小和图片的宽度一样大.
    • 把要裁剪的图片绘制到图片上下文当中.
    • 从上下文当中取出图片.
    • 关闭上下文.
    - (void)viewDidLoad {
        [super viewDidLoad];
        //1.加载图片
        UIImage *image = [UIImage imageNamed:@"阿狸头像"];
        //2.生成一个跟图片相同大小图片上下文
        UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
        //3.在上下文添加一个圆形裁剪区域
        UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
        //把路径设置成裁剪区域
        [path addClip];
        
        //可以加一个图形上下文的操作 旋转
        CGContextRef ctx = UIGraphicsGetCurrentContext();
        CGContextRotateCTM(ctx, M_PI_4);
        CGContextAddPath(ctx, path.CGPath);
        
        //4.把图片绘制图片上下文.
        [image drawAtPoint:CGPointZero];
        //5.生成一张图片
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
        //6.关闭图片上下文.
        UIGraphicsEndImageContext();
        self.imageV.image = newImage;
    }

    16、如何进行截屏操作?

      截屏效果实现具体思路为:把UIView的东西绘制图片上下文当中,生成一张新的图片。

      注意:UIView上的东西是不能直接画到上下文当中的.

      UIView之所以能够显示是因为内部的一个层(layer),所以我要把层上的东西渲染到UIView上面的那么怎样把图层当中的内容渲染到上下文当中直接调用layer的renderInContext:方法

    renderInContext带有一个参数, 就是要把图层上的内容渲染到哪个上下文

      截屏具体实现代码为:

    1. 开启一个图片上下文  UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0);
    2. 获取当前的上下文  CGContextRef ctx = UIGraphicsGetCurrentContext();
    3. 把控制器View的内容绘制上下文当中  [self.view.layer renderInContext:ctx];
    4. 从上下文当中取出图片  UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    5. 关闭上下文 UIGraphicsEndImageContext();
    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
        //1.开启图片上下文.
        UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0);
        //获取当前的上下文.
        CGContextRef ctx =  UIGraphicsGetCurrentContext();
        //UIView之所能够显示,是因为它内部有一个层,layer.层是通过渲染的方法,给绘制上下文.
        [self.view.layer renderInContext:ctx];
        //生成一张图片.
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
        //如何把图片转换成二进流.
        NSData *data = UIImagePNGRepresentation(newImage);
        [data writeToFile:@"/Users/gaoxinqiang/Desktop/newImage.png" atomically:YES]; 
        //关闭上下文.
        UIGraphicsEndImageContext(); 
    }
  • 相关阅读:
    2.1 String的特性
    sql语句对列进行操作
    java 集合排序
    C#抽象类及其方法的学习
    haproxy+keepalived实现高可用负载均衡
    RPC框架简易实现
    WCF中的ServiceHost初始化两种方式
    C# Stopwatch与TimeSpan详解
    HBase入门篇
    nginx+tomcat+redis完成session共享
  • 原文地址:https://www.cnblogs.com/mukekeheart/p/9354176.html
Copyright © 2020-2023  润新知