• Quartz 2d绘图


    今天看了一下Quartz 2D绘图,我只想说:不要把绘图和动画那些东西当做一个很复杂的东西,其实只要你认真看还是可以理解的。他们并不难。啰嗦了几句,现在直接进入正题:

    前提是我们必须新建一个singleviewApplication。具体新建就不多说了,然后我们自己写一个UIView的子类,然后创建子类加载到故事板中。(你也可以直接把故事板中的ViewControler的view的父类定义为你自己创建的类),然后我们的操作都是在drawRectangular:方法中实现的:(我采用的是直接修改View的父类为我自定义的类)

    下面的就是代码:我们一个一个来跟着代码理解:

    1、首先是最简单的填充颜色:

    - (void)drawRect:(CGRect)rect {
    //       setFill设置填充的颜色
            [[UIColor redColor] setFill];   //随后需要填充的颜色设置
            UIRectFill(rect);  //用当前的颜色进行填充
    }

    其中:

    setFill方法:它的作用就是设置随后填充操作用到的颜色。

    UIRectFill():该方法就是用刚才设置的颜色进行填充。

    结果如下:

    我们没有在ViewController中的修改任何属性,这只是在自定义的View中完成的。

    2、使用UIRectFrame画一个矩形:

    - (void)drawRect:(CGRect)rect {
            //setStroke设置描边的颜色
            [[UIColor redColor] setStroke];
            CGRect frame = CGRectMake(20, 30, 100, 300);
            UIRectFrame(frame);
    }

    其中:

    setStroke:是设置随后描边用到的颜色

    UIRectFrame():根据指定的rect画一个框架。

    结果如下:

    3、然后是NSString类的绘制文本方法:

    - (void)drawRect:(CGRect)rect {
            NSString *s = [NSString stringWithFormat:@"zhangsan"];
            //这里面drawAtPoint就是绘制文本的方法
            [s drawAtPoint:CGPointMake(100, 300) withAttributes:@{NSFontAttributeName:[UIFont boldSystemFontOfSize:34]}];
    }

    运行效果如下:

    其中主要用的的方法就是drawAtPoint:那个方法

    另外还有drawInRect方法。具体的方法介绍就不做过多介绍了。

    4、画三角形

    - (void)drawRect:(CGRect)rect {
        CGContextRef context = UIGraphicsGetCurrentContext();  //创建图形上下文
      CGContextSaveGState(context); //保存当前图形上下文设置 CGContextMoveToPoint(context, 20, 20); CGContextAddLineToPoint(context, 40, 100); CGContextAddLineToPoint(context, 160, 20); CGContextClosePath(context); [[UIColor redColor] setFill]; [[UIColor blackColor] setStroke]; CGContextDrawPath(context, kCGPathFillStroke);
      CGContextRestoreGState(context); //恢复图形上下文设置 }

    效果如下:

    其中遇到了新的东西:CGContextRef。不要害怕遇到CG,其实很简单。

    首先,一句

     CGContextRef context = UIGraphicsGetCurrentContext();  //创建图形上下文

    创建了上下文,什么是上下文?????你可以直接把他理解成一个Quartz 2D的绘画环境。

    就是对每个的绘画操作都需要一个绘画环境,然后才能进行绘画操作。而UIGraphicsGetCurrentContext()就是获取当前的绘画环境,

    然后里面有:

    CGContextMoveToPoint:就是绘画的起始点。

    CGContextAddLineToPoint:就是从刚才绘画起始点到你在这个函数中指定的点。

    最后用CGContextClosePath去封闭这三个点组成的图形。这样一个三角形就出来。

    NO,NO,还需要CGContextDrawPath来进行操作哦。它的作用是绘制当前路径用提供的绘制模型:这里用的kCGPathFillStroke,就是描边填充模型,其他的模型读者可以自己查看。

    忘了,忘了。还有两个方法没介绍:

    CGContextSaveGState(context):它的作用是为当前的上下文保存一个复制。官方介绍是压一份复制的当前绘画状态到绘画状态栈中。也就是压栈操作。

    CGContextRestoreGState(context):设置当前绘画状态 为最近保存的一份状态。可以理解为出栈操作。

    使用这两个函数的原因:有事后需要多次改变图形上下文对象的参数,这样两次绘制就可能相互影响,这就好像拿着蜡笔画画,每一次只能拿一个。为了防止相互影响所以要保存上下文设置,绘制完成后我们在用restore那个函数回复图形上下文。

    5、Quartz路径

    Core Graphics中有4种基本图元用于描述路径:点、线、弧和贝塞尔(Bezier)曲线。前两种都不用介绍了都见过,后面的贝塞尔曲线和弧需要说一下。

    弧:可以由圆心点、半径、起始角和结束角描述。圆是弧的一个特例。只需要这只起始角度为0,结束角度为360就可以了。

    贝塞尔曲线:任何一条曲线都可以通过与他相切的控制线两端的点的位置定义。具体我也不太理解,先没看就。如果你理解的深刻可以给我说一下。

    6、坐标变换

    首先要理解:在Quartz 2D中的坐标系和UIKit坐标系是相反的。什么意思勒?就是

    Quartz 2D坐标系原点在左下角,x向右为正方向,y向上为正方向

    UIKit坐标系原点在左上角,x向右为正方向,y向下为正方向。

    现在就让我们继续看看坐标变换把。

    --------------------------------为了书写方便下面的方法我直接提取出来了,只需要在drawRect:方法里面调用就行了--------------------------------

    7、画一张图片:

    //反向image
    - (void)drawImage {
        /*
         图形的另一种操作就是变换:包括评议、缩放和旋转等形式的变换
         Quartz 2D坐标系和UIKit坐标系是相反的。
         */
        CGContextRef context = UIGraphicsGetCurrentContext();
        UIImage *image = [UIImage imageNamed:@"test"];
        CGImageRef cgImage = image.CGImage;
        CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
        CGContextDrawImage(context, rect, cgImage);
        //由于坐标系相反,所以图形是返回来的。
    }

    运行一下看看:

    这图片怎么长这样,我开始也纳闷,后来知道了,原来它是倒着放的。因为什么呢?因为Quartz 2D坐标系和UIKit的相反,所以会造成这种情况。

    先不管反正了。反正画了一个图片到view上。看看代码。应该不用过多介绍了。CGContextDrawImage和刚才的CGContextDrawPath类似,只不过画的东西不一样罢了。里面要说下CGImageRef:它是一个结构体:封装了位图图像的信息。而UIImaged的CGImage就是返回的这样的实例。其他不说了。

    8、2D图形基本变换:

    有平移变换、缩放变换、旋转变换、x轴对称变换、y轴对称变换、坐标原点对称变换。具体的都不详细介绍了。应该都能理解那些变换的意思。

    9、CTM变换矩阵

    不要看着CTM几个字母就害怕,其实它就是current transformation matrix的简称。就是当前矩阵变换。Quartz 2D提供了多种形式的变换,其中主要是CTM和仿射(Affine)变换。看一下CTM吧。

    CTM主要涉及的函数有:

    CGContextRotateCTM:旋转变换

    CGContextScaleCTM:缩放变换

    CGContextTranslateCTM:平移变换

    举个简单栗子:

    - (void)tanslateImage {
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextSaveGState(context);
          UIImage *image = [UIImage imageNamed:@"test"];
        CGImageRef cgImage = image.CGImage;
    //    CGContextTranslateCTM(context, 0, image.size.height);
    //    CGContextScaleCTM(context, -1, 1);
    
        CGContextTranslateCTM(context, 50, 50);   //移动它的位置到100,50
    //    CGContextRotateCTM(context, 360);
        CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
        CGContextDrawImage(context, rect, cgImage);
        CGContextRestoreGState(context);
    }

    看看结果:

    使用了CGContextTranslateCTM,我们可以进行位置调整。这里我把位置向右向下移动了50.就成这样了。哪些旋转和缩小就不说了,自己尝试一把。

    这里给出来代码,可以试试:

    /**旋转变换*/

    #define radians(x) (x*M_PI/180)

    - (void)rotateImage {
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextSaveGState(context);
        UIImage *image = [UIImage imageNamed:@"test"];
        CGImageRef cgImage = image.CGImage;
        CGContextRotateCTM(context, radians(-45.));
        CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
        CGContextDrawImage(context, rect, cgImage);
        CGContextRestoreGState(context);
    }
    /**缩小变换*/
    - (void)scaleImage {
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextSaveGState(context);
        UIImage *image = [UIImage imageNamed:@"test"];
        CGImageRef cgImage = image.CGImage;
        CGContextScaleCTM(context, .2, .40);   //设置缩小大小
        CGRect rect = CGRectMake(0, image.size.height, image.size.width, image.size.height);
        CGContextDrawImage(context, rect, cgImage);
    }

    最后再给出一个比较厉害的让你的图片旋转过来的方法:

    //正向image
    - (void)drawNormalImage {
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextSaveGState(context);    //保留初始上下文
        UIImage *image = [UIImage imageNamed:@"test"];
        CGImageRef cgImage = image.CGImage;
        
        CGContextTranslateCTM(context, 0, image.size.height);  //先做平移变换
        CGContextScaleCTM(context, 1, -1);   //缩放变换x不变,y相反
        CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
        CGContextDrawImage(context, rect, cgImage);
        CGContextRestoreGState(context);   //还原初始上下文
    //    CGContextScaleCTM(context, -1, 1); 
    //    CGContextDrawImage(context, rect, cgImage);
        
    
    }

    其实让图片正过来很简单,先做了平移变换,然后又缩放了一把,就0k了。

    ------------------------------------------------看累了吗?继续看。马上完--------------------------------

    仿射变换(Affine)

    也是一种2D变换,他可以重用变换,经过多次变换,每一种变换都可以用矩阵表示,通过多次矩阵相乘得到最后结果。(矩阵太难了。。。。。。。。。没关系,理解就行了)

    下面是一些访射变换函数:

    CGAffineMakeRotation:创建新的旋转变换矩阵

    CGAffineMakeScale:创建新的 缩放矩阵函数

    CGAffineMakeTranslation:创建新的平移矩阵

    CGAffineTransformRotate:旋转矩阵

    CGAffineTransformScale:缩放矩阵

    CGAffineTransformTranslate:平移矩阵

    CGContextConcatCTM:连接到CTM变换。

    这么多函数。这么多函数。这么多函数。太难了。。。。。。不过没事。看看下面的例子你应该就可以理解了。

    - (void)normalImageByAffine {
        UIImage *image = [UIImage imageNamed:@"test"];
        CGImageRef cgImage = image.CGImage;
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextSaveGState(context);
        CGAffineTransform myAffine = CGAffineTransformMakeTranslation(0, image.size.height);
        
        
        myAffine = CGAffineTransformScale(myAffine, 1, -1);
        CGContextConcatCTM(context, myAffine);
        CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
        CGContextDrawImage(context, rect, cgImage);
        CGContextRestoreGState(context);
    }

    这个方法是通过仿射变换把刚刚倒过来的图形放正。可以看一下:

    CGAFffineTransform就是一个结构体,可以用来接收CGAffineTransformMakeTranslate创建的仿射(平移)矩阵。

    然后又CGAffineTransformScale:通过里面的参数来设置缩放矩阵.然后用CGContextConcatCTM连接到CTM变换。这样就实现了图像的正过来。drawNormalImage方法实现的效果是一样的。

    ----------------------------------------------------这里是结束----------------------------------------------------

    没有了,这里就是结束了。

    给个代码把?好的好的。下面就是源码:写的比较乱,能看懂就行了。

    http://pan.baidu.com/s/1kTMUyX5

    谢谢百度网盘给的空间。

  • 相关阅读:
    关于requests.exceptions.SSLError: HTTPSConnectionPool(host='XXX', port=443)问题
    python Requests库总结
    fiddler实现手机抓包及手机安装证书报错“无法安装该证书 因为无法读取该证书文件”解决方法
    django接口的工作原理
    postman+newman+jenkins 持续集成搭建及使用,实现接口自动化
    Jmeter之JDBC Request及参数化
    selenium+Python中的面试总结
    UI自动化测试:页面截图的3种方法
    selenium中通过location和size定位元素坐标
    Allure+pytest生成测试报告
  • 原文地址:https://www.cnblogs.com/zhanggui/p/4741964.html
Copyright © 2020-2023  润新知