iOS Quartz 2D 学习

Quartz 2D是iOS常用绘图框架,Quartz 2D是Core Graphics框架的一部分,是一个二维图像绘制引擎。Quartz 2D在UIKit中也有封装和集成,我们日常开发时所用到的UIKit中的组件都是由Core Graphics进行绘制的。不仅如此,当我们引入UIKit框架时系统会自动引入Core Graphics框架,并且为了方便开发者使用在UIKit内部还对一些常用的绘图API进行了封装。
 一般分为以下几个步骤:
 一、获取绘图上下文
 二、创建并设置路径(1、创建路径,2、设置路径起点,3、增加路径内容)
 三、将路径添加到上下文
 四、设置上下文状态(1、设置边线颜色,2、设置填充颜色,3、设置线宽,4、设置线段连接样式,5、设置线段首位样式,6、设置虚线样式)
 五、绘制路径
 六、释放路径
图形上下文CGContextRef代表图形输出设备(也就是绘制的位置),包含了绘制图形的一些设备信息,Quartz 2D中的所有对象最终都必须绘制到图形上下文。在Quartz 2D中的绘图上下文可以是位图Bitmap、PDF、窗口Window、层Layer、打印对象Printer。

绘制图形

UIKit中默认已经为我们准备好了一个图形上下文对象,在UI控件的drawRect:方法(这个方法在loadView、viewDidLoad方法后执行)中我们可以通过UIKit封装函数UIGraphicsGetCurrentContext()方法获得这个图形上下文(注意在其他UI控件方法中无法取得这个对象),然后我们只要按照绘图步骤一步步执行即可。

绘制直线
- (void)drawRect:(CGRect)rect {
    
    // 1、获得图形上下文
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    // 2、绘制路径(相当于前面创建路径并添加路径到图形上下文两步操作)
    CGContextMoveToPoint(context, 40, 50);
    CGContextAddLineToPoint(context, 40, 100);
    CGContextSetLineWidth(context, 2.0); // 设置线宽
    
    // 3、设置图形上下文属性
    [[UIColor redColor] setStroke]; // 设置红色边框
    
    // 4、绘制路径
    CGContextDrawPath(context, kCGPathFillStroke);
}
绘制矩形
- (void)drawRect:(CGRect)rect {
    
    // 1、获得图形上下文
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    // 2、绘制路径(相当于前面创建路径并添加路径到图形上下文两步操作)
    CGContextMoveToPoint(context, 40, 50);
    CGContextAddLineToPoint(context, 40, 100);
    CGContextAddLineToPoint(context, 140, 100);
    CGContextAddLineToPoint(context, 140, 50);
    CGContextClosePath(context); // 封闭路径
    CGContextSetLineWidth(context, 2.0); // 设置线宽
    
    // 3、设置图形上下文属性
    [[UIColor redColor] setStroke]; // 设置红色边框
    [[UIColor greenColor] setFill]; // 设置绿色填充
//    [[UIColor blueColor] set]; // 同时设置填充和边框色
    
    // 4、绘制路径
    CGContextDrawPath(context, kCGPathFillStroke);
}

- (void)drawRect:(CGRect)rect {
    // 获得图形上下文
    CGContextRef context = UIGraphicsGetCurrentContext();
    //添加矩形对象
    CGRect myRect = CGRectMake(20, 50, 200, 50);
    CGContextAddRect(context, myRect);
    //设置属性
    [[UIColor redColor] set];
    //绘制
    CGContextDrawPath(context, kCGPathFillStroke);
}

// UIKit对绘图方法的封装
- (void)drawRect:(CGRect)rect {
    CGRect rect1 = CGRectMake(20, 100, 200, 50);
    CGRect rect2 = CGRectMake(20, 200, 200, 50);
    // 设置属性
    [[UIColor yellowColor] set];
    // 绘制矩形,相当于创建对象、添加对象到上下文、绘制三个步骤
    UIRectFill(rect1); // 绘制矩形(只有填充)
    [[UIColor redColor] setStroke];
    UIRectFrame(rect2); // 绘制矩形(只有边框)
}

绘制圆
- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    // 添加对象,绘制椭圆(圆形)的过程也是先创建一个矩形
    CGRect rect1 = CGRectMake(50, 50, 220.0, 200.0);
    CGContextAddEllipseInRect(context, rect1);
    // 设置属性
    [[UIColor greenColor] set];
    // 绘制
    CGContextDrawPath(context, kCGPathFillStroke);
}
绘制弧形
- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    // 添加弧形对象 x:中心点x坐标 y:中心点y坐标 radius:半径 startAngle:起始弧度endAngle:终止弧度 closewise:是否逆时针绘制,0则顺时针绘制
    CGContextAddArc(context, 160, 160, 100.0, 0.0, M_PI, 1);
    // 设置属性
    [[UIColor yellowColor] set];
    // 绘制
    CGContextDrawPath(context, kCGPathFillStroke);
}
绘制贝塞尔曲线

不规则图形我们需要利用路径。而曲线绘制属于路径绘制。Quartz 2D中曲线绘制分为两种:二次贝塞尔曲线和三次贝塞尔曲线。二次曲线只有一个控制点,而三次曲线有两个控制点。


贝塞尔曲线图.png
- (void)drawRect:(CGRect)rect {
    
    CGContextRef context = UIGraphicsGetCurrentContext();
    // 绘制曲线
    CGContextMoveToPoint(context, 40, 100); // 起始位置
    // 绘制二次贝塞尔曲线 c:图形上下文 cpx:控制点x坐标 cpy:控制点y坐标 x:结束点x坐标 y:结束点y坐标
    CGContextAddQuadCurveToPoint(context, 120, 10, 300, 100);
    
    CGContextMoveToPoint(context, 40, 500);
    // 绘制三次贝塞尔曲线 c:图形上下文 cp1x:第一个控制点x坐标 cp1y:第一个控制点y坐标 cp2x:第二个控制点x坐标 cp2y:第二个控制点y坐标 x:结束点x坐标 y:结束点y坐标
    CGContextAddCurveToPoint(context, 80, 200, 240, 400, 200, 200);
    
    //设置图形上下文属性
    [[UIColor greenColor] setFill];
    [[UIColor redColor] setStroke];
    
    //绘制路径
    CGContextDrawPath(context, kCGPathFillStroke);
}
绘制文字
- (void)drawRect:(CGRect)rect {

    //绘制到指定的区域内容
    NSString *str = @"文字绘制 哈哈哈";
    CGRect rect1 = CGRectMake(20, 100, 300, 300);
    UIFont *font = [UIFont systemFontOfSize:28];//设置字体
    UIColor *color = [UIColor blueColor];//字体颜色
    NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];//段落样式
    NSTextAlignment align = NSTextAlignmentLeft;//对齐方式
    style.alignment = align;
    [str drawInRect:rect1 withAttributes:@{NSFontAttributeName:font,NSForegroundColorAttributeName:color,NSParagraphStyleAttributeName:style}];
   
}
绘制图片
- (void)drawRect:(CGRect)rect {

    UIImage *image = [UIImage imageNamed:@"image_1.jpg"];
    // 从某一点开始绘制
    [image drawAtPoint:CGPointMake(30, 50)];
    // 绘制到指定的矩形中,注意如果大小不合适会会进行拉伸
//    [image drawInRect:CGRectMake(10, 50, 300, 450)];
    // 平铺绘制
//    [image drawAsPatternInRect:CGRectMake(0, 0, 320, 568)];
   
}

- (void)drawRect:(CGRect)rect {
    
    CGContextRef context = UIGraphicsGetCurrentContext();
    UIImage *image = [UIImage imageNamed:@"image_1.jpg"];
    CGSize size = [UIScreen mainScreen].bounds.size;
    CGContextSaveGState(context);
    // 上下文形变
    CGContextScaleCTM(context, 1.0, -1.0); // 在y轴缩放-1相当于沿着x张旋转180
    CGContextTranslateCTM(context, 0, -(size.height-(size.height-2*50-450))); // 向上平移
    // 图像绘制
    CGRect rect1 = CGRectMake(10, 50, 300, 450);
    CGContextDrawImage(context, rect1, image.CGImage);
    
    CGContextRestoreGState(context);
}
绘制渐变填充

Quartz 2D的渐变方式分为两种:
 a.线性渐变线:渐变色以直线方式从开始位置逐渐向结束位置渐变
 b.径向渐变:以中心点为圆心从起始渐变色向四周辐射,直到终止渐变色
 使用Quartz 2D绘图时我们的颜色除了使用常规的方法(如何前面CGContextSetRGBFillColor(CGContextRef context, CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha)方法)设置RGB和透明度外,颜色参数也可能是一个数组。如使用颜色空间填充时用到的CGContextSetFillColor(CGContextRef context, const CGFloat *components)方法,这个时候components数组中具体是如何存储颜色就要根据颜色空间而定,如果颜色空间使用RGB则数组中的元素四个为一组,分别是red(红)、green(绿)、blue(蓝)、alpha(透明度)。

// 线性渐变
- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    // 使用rgb颜色空间
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    
    /* 指定渐变色 space:颜色空间 components:颜色数组,注意由于指定了RGB颜色空间,
    那么四个数组元素表示一个颜色(red、green、blue、alpha),如果有四个颜色则这个数组有4*4个元素 locations:颜色所在位置(范围0~1)
    ,这个数组的个数不小于components中存放颜色的个数 count:渐变个数,等于locations的个数
    */
    CGFloat compoents[16] = {
        255.0/255.0, 86.0/255.0, 255.0/255.0, 1,
        255.0/255.0, 127.0/255.0, 255.0/255.0, 1,
        255.0/255.0, 200.0/255.0, 12.0/255.0, 1,
        1.0, 1.0, 1.0, 1.0
    };
    CGFloat locations[4] = {0, 0.3, 0.6, 1.0};
    CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, compoents, locations, 4);
    
    /* 绘制线性渐变 context:图形上下文 gradient:渐变色 startPoint:起始位置 endPoint:终止位置 options:绘制方式,
    kCGGradientDrawsBeforeStartLocation 开始位置之前就进行绘制,到结束位置之后不再绘制,
    kCGGradientDrawsAfterEndLocation开始位置之前不进行绘制,到结束点之后继续填充*/
    CGContextDrawLinearGradient(context, gradient, CGPointZero, CGPointMake(320, 300), kCGGradientDrawsAfterEndLocation);
    
    //释放颜色空间
    CGColorSpaceRelease(colorSpace);
}

// 径向渐变
- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    //使用rgb颜色空间
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    
    /* 指定渐变色
     space:颜色空间
     components:颜色数组,注意由于指定了RGB颜色空间,那么四个数组元素表示一个颜色(red、green、blue、alpha),
     如果有四个颜色则这个数组有4*4个元素
     locations:颜色所在位置(范围0~1),这个数组的个数不小于components中存放颜色的个数
     count:渐变个数,等于locations的个数
    */
    CGFloat compoents[16] = {
        248.0/255.0, 86.0/255.0, 86.0/255.0, 1,
        249.0/255.0, 127.0/255.0, 127.0/255.0, 1,
        255.0/255.0, 233.0/255.0, 127.0/255.0, 1,
        1.0, 1.0, 1.0, 1.0
    };
    CGFloat locations[4] = {0, 0.3, 0.6, 1.0};
    CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, compoents, locations, 4);
    
    /*绘制径向渐变
     context:图形上下文
     gradient:渐变色
     startCenter:起始点位置
     startRadius:起始半径(通常为0,否则在此半径范围内容无任何填充)
     endCenter:终点位置(通常和起始点相同,否则会有偏移)
     endRadius:终点半径(也就是渐变的扩散长度)
     options:绘制方式,kCGGradientDrawsBeforeStartLocation 开始位置之前就进行绘制,但是到结束位置之后不再绘制,
     kCGGradientDrawsAfterEndLocation开始位置之前不进行绘制,但到结束点之后继续填充
     */
    CGContextDrawRadialGradient(context, gradient, CGPointMake(160, 284),0, CGPointMake(165, 289), 150, kCGGradientDrawsAfterEndLocation);
    //释放颜色空间
    CGColorSpaceRelease(colorSpace);
}
叠加模式

Quartz 2D绘图时后面绘制的图像会覆盖前面的,默认情况下如果前面的被覆盖后将看不到后面的内容,但是有时候这个结果并不是我们想要的,因此在Quartz 2D中提供了填充模式供开发者配置调整。

/* 使用UIKit的封装方法进行叠加模式设置,
一般的方法当然是使用CGContextSetBlendMode(CGContextRef context, CGBlendMode mode)方法进行设置。*/
- (void)drawRect:(CGRect)rect {
        CGRect rect50 = CGRectMake(0, 130.0, 320.0, 50.0);
        CGRect rect1 = CGRectMake(0, 390.0, 320.0, 50.0);
        CGRect rect2 = CGRectMake(20, 50.0, 10.0, 250.0);
        CGRect rect3 = CGRectMake(40.0, 50.0, 10.0, 250.0);
        CGRect rect4 = CGRectMake(60.0, 50.0, 10.0, 250.0);
        CGRect rect5 = CGRectMake(80.0, 50.0, 10.0, 250.0);
        CGRect rect6 = CGRectMake(100.0, 50.0, 10.0, 250.0);
        CGRect rect7 = CGRectMake(120.0, 50.0, 10.0, 250.0);
        CGRect rect8 = CGRectMake(140.0, 50.0, 10.0, 250.0);
        CGRect rect9 = CGRectMake(160.0, 50.0, 10.0, 250.0);
        CGRect rect10 = CGRectMake(180.0, 50.0, 10.0, 250.0);
        CGRect rect11 = CGRectMake(200.0, 50.0, 10.0, 250.0);
        CGRect rect12 = CGRectMake(220.0, 50.0, 10.0, 250.0);
        CGRect rect13 = CGRectMake(240.0, 50.0, 10.0, 250.0);
        CGRect rect14 = CGRectMake(260.0, 50.0, 10.0, 250.0);
        CGRect rect15 = CGRectMake(280.0, 50.0, 10.0, 250.0);
        
        CGRect rect16 = CGRectMake(30.0, 310.0, 10.0, 250.0);
        CGRect rect17 = CGRectMake(50.0, 310.0, 10.0, 250.0);
        CGRect rect18 = CGRectMake(70.0, 310.0, 10.0, 250.0);
        CGRect rect19 = CGRectMake(90.0, 310.0, 10.0, 250.0);
        CGRect rect20 = CGRectMake(110.0, 310.0, 10.0, 250.0);
        CGRect rect21 = CGRectMake(130.0, 310.0, 10.0, 250.0);
        CGRect rect22 = CGRectMake(150.0, 310.0, 10.0, 250.0);
        CGRect rect23 = CGRectMake(170.0, 310.0, 10.0, 250.0);
        CGRect rect24 = CGRectMake(190.0, 310.0, 10.0, 250.0);
        CGRect rect25 = CGRectMake(210.0, 310.0, 10.0, 250.0);
        CGRect rect26 = CGRectMake(230.0, 310.0, 10.0, 250.0);
        CGRect rect27 = CGRectMake(250.0, 310.0, 10.0, 250.0);
        CGRect rect28 = CGRectMake(270.0, 310.0, 10.0, 250.0);
        CGRect rect29 = CGRectMake(290.0, 310.0, 10.0, 250.0);
        
        [[UIColor yellowColor] set];
        UIRectFill(rect50);

        [[UIColor greenColor] setFill];
        UIRectFill(rect1);
        
        [[UIColor redColor] setFill];
        UIRectFillUsingBlendMode(rect2, kCGBlendModeClear);
        UIRectFillUsingBlendMode(rect3, kCGBlendModeColor);
        UIRectFillUsingBlendMode(rect4, kCGBlendModeColorBurn);
        UIRectFillUsingBlendMode(rect5, kCGBlendModeColorDodge);
        UIRectFillUsingBlendMode(rect6, kCGBlendModeCopy);
        UIRectFillUsingBlendMode(rect7, kCGBlendModeDarken);
        UIRectFillUsingBlendMode(rect8, kCGBlendModeDestinationAtop);
        UIRectFillUsingBlendMode(rect9, kCGBlendModeDestinationIn);
        UIRectFillUsingBlendMode(rect10, kCGBlendModeDestinationOut);
        UIRectFillUsingBlendMode(rect11, kCGBlendModeDestinationOver);
        UIRectFillUsingBlendMode(rect12, kCGBlendModeDifference);
        UIRectFillUsingBlendMode(rect13, kCGBlendModeExclusion);
        UIRectFillUsingBlendMode(rect14, kCGBlendModeHardLight);
        UIRectFillUsingBlendMode(rect15, kCGBlendModeHue);
        UIRectFillUsingBlendMode(rect16, kCGBlendModeLighten);
        
        UIRectFillUsingBlendMode(rect17, kCGBlendModeLuminosity);
        UIRectFillUsingBlendMode(rect18, kCGBlendModeMultiply);
        UIRectFillUsingBlendMode(rect19, kCGBlendModeNormal);
        UIRectFillUsingBlendMode(rect20, kCGBlendModeOverlay);
        UIRectFillUsingBlendMode(rect21, kCGBlendModePlusDarker);
        UIRectFillUsingBlendMode(rect22, kCGBlendModePlusLighter);
        UIRectFillUsingBlendMode(rect23, kCGBlendModeSaturation);
        UIRectFillUsingBlendMode(rect24, kCGBlendModeScreen);
        UIRectFillUsingBlendMode(rect25, kCGBlendModeSoftLight);
        UIRectFillUsingBlendMode(rect26, kCGBlendModeSourceAtop);
        UIRectFillUsingBlendMode(rect27, kCGBlendModeSourceIn);
        UIRectFillUsingBlendMode(rect28, kCGBlendModeSourceOut);
        UIRectFillUsingBlendMode(rect29, kCGBlendModeXOR);
}

填充模式

有时我们需要按一定的自定义样式进行填充,这种方式有点类似于贴瓷砖的方式。我们知道如果家里贴地板或瓷砖时,通常我们会先选择一种瓷砖样式,根据房间面积我们购买不同量的瓷砖。但是不管买多少,这些瓷砖的样式都是一模一样的。填充模式就是为了达到这种效果而产生的:我们只需要绘制一个瓷砖的样式,然后让程序自动调用这种样式填充指定大小的区域。
 Quartz 2D支持两种填充模式:有颜色填充和无颜色填充。两种模式使用起来区别很小,有颜色填充就是在绘制瓷砖时就指定颜色,在调用填充时就不用再指定瓷砖颜色;无颜色填充模式就是绘制瓷砖时不用指定任何颜色,在调用填充时再指定具体填充颜色。相比较无颜色填充模式而言,有颜色填充模式更加的灵活。
 如何按指定模式进行图形填充:
 1、在使用填充模式时首先要构建一个符合CGPatternDrawPatternCallback签名的方法,这个方法专门用来创建“瓷砖”。注意:如果使用有颜色填充模式,需要设置填充色。
 2、指定一个填充的颜色空间,这个颜色空间跟前面绘制渐变的颜色空间不太一样,前面创建渐变使用的颜色空间是设备无关的,我们需要基于这个颜色空间创建一个颜色空间专门用于填充(注意对于有颜色填充创建填充颜色空间参数为NULL,不用基于设备无关的颜色空间创建)。
 3、然后我们就可以使用CGPatternCreate方法创建一个填充模式,创建填充模式时需要注意其中的参数)。
 4、最后调用CGContextSetFillPattern方法给图形上下文指定填充模式(这个时候注意最后一个参数,如果是有颜色填充模式最后一个参数为透明度alpa的地址,对于无颜色填充模式最后一个参数是当前填充颜色空间的颜色数组)。
 5、绘制图形。
 6、释放资源。

- (void)drawRect:(CGRect)rect {
    
    CGContextRef context = UIGraphicsGetCurrentContext();
    
//    [self drawBackgroundWithColoredPattern:context];

    [self drawBackgroundWithPattern:context];
}

#pragma mark - 有颜色填充模式
void drawColoredTile(void *info, CGContextRef context){
    //有颜色填充,这里设置填充色
    CGContextSetRGBFillColor(context, 0/255.0, 255.0/255.0, 0/255.0, 1);
    CGContextFillRect(context, CGRectMake(0, 0, 30, 30));
    CGContextFillRect(context, CGRectMake(30, 30, 30, 30));
}

-(void)drawBackgroundWithColoredPattern:(CGContextRef) context{
    //设备无关的颜色空间
//    CGColorSpaceRef rgbSpace = CGColorSpaceCreateDeviceRGB();
    //模式填充颜色空间,注意对于有颜色填充模式,这里传NULL
    CGColorSpaceRef colorSpace = CGColorSpaceCreatePattern(NULL);
    //将填充色颜色空间设置为模式填充的颜色空间
    CGContextSetFillColorSpace(context, colorSpace);
    
    //填充模式回调函数结构体
    CGPatternCallbacks callback = {0,  &drawColoredTile, NULL};
    /*填充模式
     info://传递给callback的参数
     bounds:瓷砖大小
     matrix:形变
     xStep:瓷砖横向间距
     yStep:瓷砖纵向间距
     tiling:贴砖的方法
     isClored:绘制的瓷砖是否已经指定了颜色(对于有颜色瓷砖此处指定位true)
     callbacks:回调函数
     */
    CGPatternRef pattern = CGPatternCreate(NULL, CGRectMake(0, 0, 2*30, 2*30), CGAffineTransformIdentity, 2*30+5, 2*30+5, kCGPatternTilingNoDistortion, true, &callback);
    
    CGFloat alpha = 1;
    //注意最后一个参数对于有颜色瓷砖指定为透明度的参数地址,对于无颜色瓷砖则指定当前颜色空间对应的颜色数组
    CGContextSetFillPattern(context, pattern, &alpha);
    
    UIRectFill(CGRectMake(0, 0, 320, 568));
    
//    CGColorSpaceRelease(rgbSpace);
    CGColorSpaceRelease(colorSpace);
    CGPatternRelease(pattern);
}


#pragma mark - 无颜色填充模式
//填充瓷砖的回调函数(必须满足CGPatternCallbacks签名)
void drawTile(void *info, CGContextRef context){
    CGContextFillRect(context, CGRectMake(0, 0, 30, 30));
    CGContextFillRect(context, CGRectMake(30, 30, 30, 30));
}

-(void)drawBackgroundWithPattern:(CGContextRef)context{
    //设备无关的颜色空间
    CGColorSpaceRef rgbSpace = CGColorSpaceCreateDeviceRGB();
    //模式填充颜色空间
    CGColorSpaceRef colorSpace = CGColorSpaceCreatePattern(rgbSpace);
    //将填充色颜色空间设置为模式填充的颜色空间
    CGContextSetFillColorSpace(context, colorSpace);
    
    //填充模式回调函数结构体
    CGPatternCallbacks callback = {0, &drawTile, NULL};
    /*填充模式
     info://传递给callback的参数
     bounds:瓷砖大小
     matrix:形变
     xStep:瓷砖横向间距
     yStep:瓷砖纵向间距
     tiling:贴砖的方法(瓷砖摆放的方式)
     isClored:绘制的瓷砖是否已经指定了颜色(对于无颜色瓷砖此处指定位false)
     callbacks:回调函数
     */
    CGPatternRef pattern = CGPatternCreate(NULL, CGRectMake(0, 0, 2*30, 2*30), CGAffineTransformIdentity, 2*30+5, 2*30+5, kCGPatternTilingNoDistortion, false, &callback);
    
    CGFloat components[] = {0/255.0, 255.0/255.0, 0/255.0, 1.0};
    //注意最后一个参数对于无颜色填充模式指定为当前颜色空间颜色数据
    CGContextSetFillPattern(context, pattern, components);
    //    CGContextSetStrokePattern(context, pattern, components);
    UIRectFill(CGRectMake(0, 0, 320, 568));
    
    CGColorSpaceRelease(rgbSpace);
    CGColorSpaceRelease(colorSpace);
    CGPatternRelease(pattern);
}


drawTile回调方法中不要使用UIKit封装方法进行图形绘制(例如UIRectFill等),由于这个方法由Core Graphics内部调用,而Core Graphics考虑到跨平台问题,内部是不允许调用UIKit方法的
上下文变换

UIKit开发中UIView有一个transform属性用于控件的形变,其实在绘图中我们也经常用到图形形变,这个时候可以借助图形上下文的形变方法来完成。在弄清形变之前我们要清楚图形上下文的坐标原点,因为无论是位移还是旋转都是相对于坐标原点进行的。Quartz 2D的坐标系同UIKit并不一样,它的坐标原点在屏幕左下方,但是为了统一编程方式,UIKit对其进行了转换,坐标原点统一在屏幕左上角。注意在设置图形上下文形变之前一定要注意保存上下文的初始状态,在使用完之后进行恢复。否则在处理多个图形形变的时候很容易弄不清楚到底是基于怎样的坐标系进行绘图,容易找不到原点。

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    // 保存初始状态
    CGContextSaveGState(context);
    // 形变:图形上下文向右平移100
    CGContextTranslateCTM(context, 100, 0);
    // 形变:缩放0.8
    CGContextScaleCTM(context, 0.8, 0.8);
    // 形变:旋转
    CGContextRotateCTM(context, M_PI_4 / 4);
    // 绘制图片
    UIImage *image = [UIImage imageNamed:@"image_2.jpg"];
    [image drawInRect:CGRectMake(0, 60, 200, 200)];
    
    // 恢复到初始状态
    CGContextRestoreGState(context);
}
刷新视图

在UIView的drawRect:中绘制的图形会在控件显示的时候调用(而且显示时会重绘所有图形),有时候我们希望绘制内容的显示是实时的,此时我们就需要调用绘图方法重新绘制,但是在iOS开发中不允许开发者直接调用drawRect:方法,刷新绘制内容需要调用setNeedsDisplay方法。

// .h文件
#import <UIKit/UIKit.h>

@interface PXShowView : UIView

@property (nonatomic, assign) CGFloat fontSize;

@end

// .m文件
#import "PXShowView.h"

@implementation PXShowView

- (void)drawRect:(CGRect)rect {
    NSString *str = @"你号OC!";
    UIFont *font = [UIFont systemFontOfSize:self.fontSize];
    UIColor *fontColor = [UIColor redColor];
    [str drawInRect:CGRectMake(100, 120, 300, 200) withAttributes:@{NSFontAttributeName:font, NSForegroundColorAttributeName:fontColor}];
    
}
@end

// 控制器.m文件
#import "ViewController.h"
#import "PXShowView.h"

@interface ViewController ()

@property (nonatomic, strong) PXShowView *showView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.showView = [[PXShowView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.showView.backgroundColor = [UIColor whiteColor];
    self.showView.fontSize = 12.0;
    [self.view addSubview:self.showView];
    
    UIButton *but1 = [[UIButton alloc] initWithFrame:CGRectMake(0, 20, 30, 30)];
    [but1 setTitle:@"30" forState:UIControlStateNormal];
    [but1 setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
    [but1 addTarget:self action:@selector(reloadShowView:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:but1];
    
    UIButton *but2 = [[UIButton alloc] initWithFrame:CGRectMake(0, 80, 30, 30)];
    [but2 setTitle:@"50" forState:UIControlStateNormal];
    [but2 setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
    [but2 addTarget:self action:@selector(reloadShowView:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:but2];

}

- (void) reloadShowView:(UIButton *) sender {
    self.showView.fontSize = [sender.titleLabel.text floatValue];
    [self.showView setNeedsDisplay];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

@end

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

推荐阅读更多精彩内容