# 学习Swift你应该懂点Core Graphics (长篇高能)

语言：zh-CN
原文：[学习Swift你应该懂点Core Graphics (长篇高能)](<https://allluckly.cn/%E6%8A%95%E7%A8%BF/tuogao29>)
中文：[中文 HTML](<https://allluckly.cn/%E6%8A%95%E7%A8%BF/tuogao29>)
English: [English HTML](<https://allluckly.cn/en/journal/2016-03-04-core-graphics-for-swift-developers-an-in-depth-introduction/>)
发布于: 2016-03-04T00:00:00+08:00
内容更新于: 2016-03-04T00:00:00+08:00
署名：大石头布

从获取绘图上下文和建立路径开始，以 Swift 演示矩形、圆、曲线、文字和图片绘制，再扩展线性与径向渐变、混合模式、坐标变换以及位图和 PDF 输出。

原文署名大石头布，Bison 编辑收录。

[原文所列来源](<http://www.jianshu.com/p/491b50cb19cb>)

**阅读前** 这是 2015–2017 年间的技术记录，本次整理未重新运行验证示例。原文发表于 2016 年，采用当时的 Swift 与 Core Graphics 接口写法。

编辑：[Bison](<https://allluckly.cn/>)  
 投稿：[大石头布](<http://www.jianshu.com/p/491b50cb19cb>)

Core Graphics是基于C的API，可以用于一切绘图

Quartz 2D是Core Graphics框架的一部分，是一个强大的二维图像绘制引擎。Quartz 2D在UIKit中也有很好的封装和集成，我们日常开发时所用到的UIKit中的组件都是由Core Graphics进行绘制的。不仅如此，当我们引入UIKit框架时系统会自动引入Core Graphics框架，并且为了方便开发者使用在UIKit内部还对一些常用的绘图API进行了封装。  
 不废话，直接来看利用Core Graphics怎么绘图。

### 一般步骤

1、获取绘图上下文  
 2、创建并设置路径  
 3、将路径添加到上下文  
 4、设置上下文状态  
 5、绘制路径

> 图形上下文CGContextRef代表图形输出设备（也就是绘制的位置），包含了绘制图形的一些设备信息。Quartz 2D中所有对象都必须绘制在图形上下文

### 基本图形的绘制

希望大家打开Xcode跟着练习  
 创建一个Single View Application ，新建一个继承自View的子类CGView

```
import UIKit

class CGView: UIView {


    override func drawRect(rect: CGRect) {
        super.drawRect(rect)
        // 1、 获取上下文对象
        let context = UIGraphicsGetCurrentContext()

        // 2、 创建路径对象
        let path = CGPathCreateMutable()
        CGPathMoveToPoint(path, nil, 10,30)
        CGPathAddLineToPoint(path, nil, 10, 100)
        CGPathAddLineToPoint(path, nil, 150, 100)

        // 3、 添加路径到图形上下文 
        CGContextAddPath(context, path)

        // 4、 设置图形上下文状态属性
        CGContextSetRGBStrokeColor(context, 1, 0, 1, 1) //设置笔触颜色
        CGContextSetRGBFillColor(context, 1, 1, 0, 1)   //设置填充色

        CGContextSetLineWidth(context, 5)   //设置线条宽度
        CGContextSetLineCap(context, CGLineCap.Round ) // 设置顶点样式
        CGContextSetLineJoin(context, CGLineJoin.Round) //设置连接点样式
        /*设置线段样式
        phase:虚线开始的位置  这里设置为0
        lengths:虚线长度间隔
        count:虚线数组元素个数
        */
        let lengths: [CGFloat] = [5,7] //长度间隔
        CGContextSetLineDash(context, 0 , lengths, 2)

        let color = UIColor.grayColor().CGColor //颜色转化，由于Quartz 2D跨平台，所以其中不能使用UIKit中的对象，但是UIkit提供了转化方法
        /*设置阴影
        context:图形上下文
        offset:偏移量
        blur:模糊度
        color:阴影颜色
        */
        CGContextSetShadowWithColor(context, CGSizeMake(2, 2), 0.8, color)

        // 5、 绘制图像到指定图形上下文
        /*
        CGPathDrawingMode是填充方式,枚举类型
        Fill:只有填充（非零缠绕数填充），不绘制边框
        EOFill:奇偶规则填充（多条路径交叉时，奇数交叉填充，偶交叉不填充）
        Stroke:只有边框
        FillStroke：既有边框又有填充
        EOFillStroke：奇偶填充并绘制边框
        */
        CGContextDrawPath(context, CGPathDrawingMode.FillStroke) //最后一个参数是填充类型
    }
}
```

UIKit已经为我们准备好了一个上下文，在drawRect:方法（这个方法在loadView、viewDidLoad方法后执行）中我们可以通过UIKit封装函数UIGraphicsGetCurrentContext()方法获得这个图形上下文  
 然后我们按照上面说的步骤一步一步的执行下来得到结果：  
 然后在vc的viewDidLoad中加入代码

```
let cgView = CGView(frame:CGRectMake(0,44,160,110) )
cgView.backgroundColor = UIColor.whiteColor()
self.view.addSubview(cgView)
```

得到结果如图：

![1](<https://allluckly.cn/images/blog/tuogao/tougao29/1.png>)

上面的步骤略显复杂，其实代码还可以简化  
 我们再创建一个继承自UIView的子类 CGView2

```
import UIKit

class CGView2: UIView {


    override func drawRect(rect: CGRect) {
        super.drawRect(rect)

        // 1、 获取上下文对象
        let context = UIGraphicsGetCurrentContext()

        // 2、 绘制路径（相当于前面创建路径并添加路径到图形上下文两步操作）
        CGContextMoveToPoint(context, 10, 30)
        CGContextAddLineToPoint(context, 10, 100)
        CGContextAddLineToPoint(context, 150, 100)

        //封闭路径:直接调用路径封闭方法
        CGContextClosePath(context)

        // 3、 设置图形上下文属性
        UIColor.redColor().setStroke()
        UIColor.yellowColor().setFill()

        // 4、 绘制路径
        CGContextDrawPath(context, CGPathDrawingMode.FillStroke)

    }

}
```

上面代码精简了许多，Core Graphics 内部对创建对象添加到上下文这两步操作进行了封装，可以一步完成 继续在刚的vc的viewDidLoad:中加入代码

```
let cgView = CGView(frame:CGRectMake(0,44,160,110) )
cgView.backgroundColor = UIColor.whiteColor()
self.view.addSubview(cgView)

let cgView2 = CGView2(frame:CGRectMake(160,44,160,110))
cgView2.backgroundColor = UIColor.whiteColor()
self.view.addSubview(cgView2)
```

得到结果:

![1](<https://allluckly.cn/images/blog/tuogao/tougao29/2.png>)

### 绘制矩形

我们再来创建一个继承自 UIView的子类CGView3  
 /\*\* 画矩形

```
- parameter context: 上下文对象
*/
func drawRectWithContext(context:CGContextRef){
    let rect = CGRectMake(20, 0 , 280, 50)
    CGContextAddRect(context, rect)
    UIColor.blueColor().set()
    CGContextDrawPath(context, CGPathDrawingMode.FillStroke)
}
```

然后在drawrect中调用此方法

```
override func drawRect(rect: CGRect) {
    super.drawRect(rect)

    // 1、 获取上下文对象
    let context = UIGraphicsGetCurrentContext()
    //画矩形
    self.drawRectWithContext(context!)
}
在vc对应viewDidLoad中加入此View
override func viewDidLoad() {
    super.viewDidLoad()
    title = "CoreGraphics Demo"

    // Do any additional setup after loading the view, typically from a nib.
    let cgView = CGView(frame:CGRectMake(0,44,160,110) )
    cgView.backgroundColor = UIColor.whiteColor()
    self.view.addSubview(cgView)

    let cgView2 = CGView2(frame:CGRectMake(160,44,160,110))
    cgView2.backgroundColor = UIColor.whiteColor()
    self.view.addSubview(cgView2)

    let cgView3 = CGView3(frame:CGRectMake(0,164,self.view.frame.size.width,self.view.frame.size.height - 110))
    cgView3.backgroundColor = UIColor.whiteColor()
    self.view.addSubview(cgView3)

}
```

运行后结果:

![1](<https://allluckly.cn/images/blog/tuogao/tougao29/3.jpg>)

其实UIKit对绘图方法的封装，使用起来更加简单。我们可以直接使用直接在CGView3中继续添加方法  
 /\*\* 画矩形 用uikit封装的方法

```
- parameter context: 上下文对象
*/
func drawRectByUIKitWithContext(context:CGContextRef){
    let rect = CGRectMake(20, 60, 280.0, 50.0)
    let rect2 = CGRectMake(20, 120, 280.0, 50.0)

    UIColor.yellowColor().set()
    UIRectFill(rect)

    UIColor.redColor().setFill()
    UIRectFill(rect2)
}
```

然后在drawRect中调用 运行结果

![1](<https://allluckly.cn/images/blog/tuogao/tougao29/4.png>)

### 绘制椭圆和圆

继续在CGView3中添加方法

```
/**
画椭圆 宽高一样就是正圆

- parameter context: 上下文对象
*/
func drawEllipse(context:CGContextRef){
    let rect = CGRectMake(20, 180 ,100,120)

    CGContextAddEllipseInRect(context, rect)
    UIColor.purpleColor().set()
    CGContextDrawPath(context, CGPathDrawingMode.Fill)
}
```

这里宽高设置的不一样 只要把宽和高设置一样就是正圆   
 在drawRect中添加此方法  
 运行结果:

![1](<https://allluckly.cn/images/blog/tuogao/tougao29/5.png>)

### 绘制弧形

```
/**
创建弧形

- parameter context: 上下文对象
*/
func drawArc(context:CGContextRef){
    /*
    添加弧形对象
    x:中心点x坐标
    y:中心点y坐标
    radius:半径
    startAngle:起始弧度
    endAngle:终止弧度
    closewise:是否逆时针绘制，0则顺时针绘制
    */
    CGContextAddArc(context, 200, 250 , 50 , 0 , CGFloat(M_PI) , 0)
    UIColor.greenColor().set()
    CGContextDrawPath(context, CGPathDrawingMode.Fill)
}
```

和上面一样 添加到drawRect  
 运行结果

![1](<https://allluckly.cn/images/blog/tuogao/tougao29/6.png>)

### 绘制贝塞尔曲线

在Quartz 2D中曲线绘制分为两种：二次贝塞尔曲线和三次贝塞尔曲线。二次曲线只有一个控制点，而三次曲线有两个控制点，如下图所示：

![1](<https://allluckly.cn/images/blog/tuogao/tougao29/7.png>)

下面演示下绘制方法

```
/**
绘制贝塞尔曲线

- parameter context: 上下文对象
*/
func drawCurve(context:CGContextRef){
    //绘制曲线
    CGContextMoveToPoint(context, 20, 310) //移动到起始位置
    /*
    绘制二次贝塞尔曲线
    c:图形上下文
    cpx:控制点x坐标
    cpy:控制点y坐标
    x:结束点x坐标
    y:结束点y坐标
    */
    CGContextAddQuadCurveToPoint(context, 220, 310 , 100, 400)

    CGContextMoveToPoint(context, 230, 310)
    /*绘制三次贝塞尔曲线
    c:图形上下文
    cp1x:第一个控制点x坐标
    cp1y:第一个控制点y坐标
    cp2x:第二个控制点x坐标
    cp2y:第二个控制点y坐标
    x:结束点x坐标
    y:结束点y坐标
    */
    CGContextAddCurveToPoint(context, 100 , 330, 400, 410, 300, 500);

    UIColor.yellowColor().setFill()
    UIColor.redColor().setStroke()
    CGContextDrawPath(context, CGPathDrawingMode.FillStroke)
}
```

上面所有方法加入到drawRect代码如下:

```
override func drawRect(rect: CGRect) {
    super.drawRect(rect)

    // 1、 获取上下文对象
    let context = UIGraphicsGetCurrentContext()
    //画矩形
    self.drawRectWithContext(context!)
    //画矩形 用uikit封装的方法
    self.drawRectByUIKitWithContext(context!)
    //画椭圆 宽高一样就是正圆
    self.drawEllipse(context!)
    //画弧形
    self.drawArc(context!)
    //绘制 二次三次贝塞尔曲线
    self.drawCurve(context!)
}
```

结果:

![1](<https://allluckly.cn/images/blog/tuogao/tougao29/8.png>)

### 文字绘制

Core Graphics不仅可以画图还能绘制文字这个页面已经放不下了 开一个新的页面 继续，后面我会把所有代码上传github 供大家参考 创建一个新的继承自UIView的类CGView4  
 添加下面方法  
 /\*\* 文字绘制

```
- parameter context: 上下文对象
*/
func drawText(context:CGContextRef){
    let str = "使用CoreGraphics进行文字绘制使用CoreGraphics进行文字绘制使用CoreGraphics进行文字绘制使用CoreGraphics进行文字绘制使用CoreGraphics进行文字绘制使用CoreGraphics进行文字绘制使用CoreGraphics进行文字绘制使用CoreGraphics进行文字绘制使用CoreGraphics进行文字绘制"
    let rect = CGRectMake(20, 20, 280, 200)
    let font = UIFont.systemFontOfSize(16)
    let color = UIColor.redColor()
    let style = NSMutableParagraphStyle()
    style.alignment = NSTextAlignment.Left
    (str as NSString).drawInRect(rect, withAttributes: [NSFontAttributeName:font,NSForegroundColorAttributeName:color,NSParagraphStyleAttributeName:style])

}
```

声明了一个字符串，设置了绘制区间、字体颜色段落属性等 当然还可以设置更多其他属性，设置属性参考我的另一篇文章[Swift之CoreText排版神器(长篇高能)](<https://allluckly.cn/%E6%8A%95%E7%A8%BF/tuogao29>)  
 画好之后 还在在drawRect中调用  
 override func drawRect(rect: CGRect) { super.drawRect(rect)

```
    let context = UIGraphicsGetCurrentContext()

    //文字绘制
    self.drawText(context!)

}
```

加入新的vc的viewDidLoad方法

```
override func viewDidLoad() {
    super.viewDidLoad()
    let cgView4 = CGView4(frame:CGRectMake(0,64,self.view.frame.width,self.view.frame.height))
    cgView4.backgroundColor = UIColor.whiteColor()
    self.view.addSubview(cgView4)

}
```

结果

![1](<https://allluckly.cn/images/blog/tuogao/tougao29/9.jpg>)

文字已经正确的绘制到视图上

### 图像绘制

当然Core Graphics也能绘制图像  
 很简单

```
/**
图像绘制

- parameter context: 上下文对象
*/
func drawImage(context:CGContextRef){

    let img = UIImage(named: "ddla")
    //绘制到指定的矩形中，注意如果大小不合适会会进行拉伸
    img?.drawInRect(CGRectMake(0, 200, 100, 80))
    //从某一点开始绘制
    img?.drawAtPoint(CGPoint(x: 0, y: 320))

}
```

可以从某点开始绘制也可以指定位置区域

![1](<https://allluckly.cn/images/blog/tuogao/tougao29/10.jpg>)

### 绘制渐变

Quartz 2D的渐变方式分为两种：

线性渐变线：渐变色以直线方式从开始位置逐渐向结束位置渐变

径向渐变：以中心点为圆心从起始渐变色向四周辐射，直到终止渐变色

在iOS中绘制渐变还需要注意一点就是指定颜色空间，所谓颜色空间就是不同颜色在不同的维度上取值最终组成一种颜色的过程。就拿RGB来说，如果将红色、绿色、蓝色看成是x、y、z轴坐标系，那么在三个坐标上分别取0~255范围内的不同值则可以组成各类颜色。当然，不同颜色空间的“坐标系”也是不同的（也就是说颜色表示的方式是不同的），常用的颜色空间除了RGB还有CMYK（印刷业常用这种颜色模式）、Gray

下面的代码分别演示了两种渐变方式，具体渐变绘制函数参数代码中已经注释的很清楚了：

### 线性渐变

新建一个CGView5

```
class CGView5: UIView {

    override func drawRect(rect: CGRect) {
        super.drawRect(rect)
        let context = UIGraphicsGetCurrentContext()

        //绘制线性渐变
        self.drawLinearGradient(context!)

    }

    /**
    绘制线性渐变

    - parameter context: 上下文
    */
    func drawLinearGradient(context:CGContextRef){

        //使用rgb颜色空间
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        /*
        指定渐变色
        space:颜色空间
        components:颜色数组,注意由于指定了RGB颜色空间，那么四个数组元素表示一个颜色（red、green、blue、alpha），
        如果有三个颜色则这个数组有4*3个元素
        locations:颜色所在位置（范围0~1），这个数组的个数不小于components中存放颜色的个数
        count:渐变个数，等于locations的个数
        */
        let compoents:[CGFloat] = [ 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,
        1.0,1.0,1.0,1.0]

        let locations:[CGFloat] = [0,0.4,1]
        let gradient = CGGradientCreateWithColorComponents(colorSpace, compoents, locations, locations.count)
        /*
        绘制线性渐变
        context:图形上下文
        gradient:渐变色
        startPoint:起始位置
        endPoint:终止位置
        options:绘制方式,DrawsBeforeStartLocation 开始位置之前就进行绘制，到结束位置之后不再绘制，
        DrawsAfterEndLocation开始位置之前不进行绘制，到结束点之后继续填充
        */
        CGContextDrawLinearGradient(context, gradient , CGPointZero, CGPointMake(self.frame.size.width,100), CGGradientDrawingOptions.DrawsAfterEndLocation)
    }

}
```

然后添加到新的vc中

```
let ctView = CGView5(frame:CGRectMake(0,64,self.view.frame.width,100))
ctView.backgroundColor = UIColor.whiteColor()
self.view.addSubview(ctView)
```

效果

![1](<https://allluckly.cn/images/blog/tuogao/tougao29/11.png>)

### 径向渐变

新建一个CGView6

/\*\* 径向渐变绘制

```
- parameter context: 上下文
*/
func drawRadialGradient(context:CGContextRef){

    //使用rgb颜色空间
    let colorSpace = CGColorSpaceCreateDeviceRGB()
    /*
    指定渐变色
    space:颜色空间
    components:颜色数组,注意由于指定了RGB颜色空间，那么四个数组元素表示一个颜色（red、green、blue、alpha），
    如果有三个颜色则这个数组有4*3个元素
    locations:颜色所在位置（范围0~1），这个数组的个数不小于components中存放颜色的个数
    count:渐变个数，等于locations的个数
    */
    let compoents:[CGFloat] = [ 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,
    1.0,1.0,1.0,1.0]

    let locations:[CGFloat] = [0,0.4,1]
    let gradient = CGGradientCreateWithColorComponents(colorSpace, compoents, locations, locations.count)
    /*
    绘制线性渐变
    context:图形上下文
    gradient:渐变色
    startPoint:起始位置
    startRadius:起始半径（通常为0，否则在此半径范围内容无任何填充）
    endCenter:终点位置（通常和起始点相同，否则会有偏移）
    endRadius:终点半径（也就是渐变的扩散长度）
    options:绘制方式,DrawsBeforeStartLocation 开始位置之前就进行绘制，到结束位置之后不再绘制，
    DrawsAfterEndLocation开始位置之前不进行绘制，到结束点之后继续填充
    */
    CGContextDrawRadialGradient(context, gradient, CGPointMake(100,100), 0, CGPointMake(105, 105), 80, CGGradientDrawingOptions.DrawsAfterEndLocation)
}
```

将此方法加入到drawRect中   
 将view加到vc中

```
override func viewDidLoad() {
    super.viewDidLoad()
    let ctView = CGView5(frame:CGRectMake(0,64,self.view.frame.width,100))
    ctView.backgroundColor = UIColor.whiteColor()
    self.view.addSubview(ctView)

    let ctView1 = CGView6(frame:CGRectMake(0,100,self.view.frame.width,self.view.frame.height-100))
    ctView1.backgroundColor = UIColor.whiteColor()
    self.view.addSubview(ctView1)
}
```

运行效果

![1](<https://allluckly.cn/images/blog/tuogao/tougao29/12.png>)

### 渐变填充

有时候 我们需要在一个矩形中填充渐变，在此可以利用渐变裁切来完成  
 继续在CGView6中添加以下方法  
 /\*\* 绘制了一个矩形填充的渐变

```
- parameter context: 上下文对象
*/
func drawRectWithLinearGradientFill(context:CGContextRef){
    //使用rgb颜色空间
    let colorSpace = CGColorSpaceCreateDeviceRGB()
    //裁切处一块矩形用于显示，注意必须先裁切再调用渐变
    //CGContextClipToRect(context, CGRectMake(20, 150, 280, 300))
    //裁切还可以使用UIKit中对应的方法
    UIRectClip(CGRectMake(20, 250, 300, 300))

    let compoents:[CGFloat] = [ 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,
    1.0,1.0,1.0,1.0]

    let locations:[CGFloat] = [0,0.4,1]
    let gradient = CGGradientCreateWithColorComponents(colorSpace, compoents, locations, locations.count)
    CGContextDrawLinearGradient(context, gradient, CGPointMake(20, 250), CGPointMake(320, 300), CGGradientDrawingOptions.DrawsAfterEndLocation);

}
```

结果

![1](<https://allluckly.cn/images/blog/tuogao/tougao29/13.png>)

### 叠加模式

使用Quartz 2D绘图时后面绘制的图像会覆盖前面的，默认情况下如果前面的被覆盖后将看不到后面的内容，但是有时候这个结果并不是我们想要的，因此在Quartz 2D中提供了填充模式供开发者配置调整。由于填充模式类别特别多。  
 因此下面以一个例子来说明：  
 新建一个CGView7  
 import UIKit

```
class CGView7: UIView {

    //相信大家对比代码和显示效果并不难发现每种叠加的效果。例子中只是使用UIKit的封装方法进行叠加模式设置，更一般的方法当然是使用CGContextSetBlendMode(CGContextRef context, CGBlendMode mode)方法进行设置。
    override func drawRect(rect: CGRect) {
        super.drawRect(rect)
        let rect0 = CGRectMake(0, 130.0, 320.0, 50.0)
        let rect1 = CGRectMake(0, 390.0, 320.0, 50.0)

        let rect2=CGRectMake(20, 50.0, 10.0, 250.0)
        let rect3=CGRectMake(40.0, 50.0, 10.0, 250.0)
        let rect4=CGRectMake(60.0, 50.0, 10.0, 250.0)
        let rect5=CGRectMake(80.0, 50.0, 10.0, 250.0)
        let rect6=CGRectMake(100.0, 50.0, 10.0, 250.0)
        let rect7=CGRectMake(120.0, 50.0, 10.0, 250.0)
        let rect8=CGRectMake(140.0, 50.0, 10.0, 250.0)
        let rect9=CGRectMake(160.0, 50.0, 10.0, 250.0)
        let rect10=CGRectMake(180.0, 50.0, 10.0, 250.0)
        let rect11=CGRectMake(200.0, 50.0, 10.0, 250.0)
        let rect12=CGRectMake(220.0, 50.0, 10.0, 250.0)
        let rect13=CGRectMake(240.0, 50.0, 10.0, 250.0)
        let rect14=CGRectMake(260.0, 50.0, 10.0, 250.0)
        let rect15=CGRectMake(280.0, 50.0, 10.0, 250.0)

        let rect16=CGRectMake(30.0, 310.0, 10.0, 250.0)
        let rect17=CGRectMake(50.0, 310.0, 10.0, 250.0)
        let rect18=CGRectMake(70.0, 310.0, 10.0, 250.0)
        let rect19=CGRectMake(90.0, 310.0, 10.0, 250.0)
        let rect20=CGRectMake(110.0, 310.0, 10.0, 250.0)
        let rect21=CGRectMake(130.0, 310.0, 10.0, 250.0)
        let rect22=CGRectMake(150.0, 310.0, 10.0, 250.0)
        let rect23=CGRectMake(170.0, 310.0, 10.0, 250.0)
        let rect24=CGRectMake(190.0, 310.0, 10.0, 250.0)
        let rect25=CGRectMake(210.0, 310.0, 10.0, 250.0)
        let rect26=CGRectMake(230.0, 310.0, 10.0, 250.0)
        let rect27=CGRectMake(250.0, 310.0, 10.0, 250.0)
        let rect28=CGRectMake(270.0, 310.0, 10.0, 250.0)
        let rect29=CGRectMake(290.0, 310.0, 10.0, 250.0)

        UIColor.yellowColor().set()
        UIRectFill(rect0)
        UIColor.greenColor().set()
        UIRectFill(rect1)
        UIColor.redColor().set()
        UIRectFillUsingBlendMode(rect2, CGBlendMode.Clear )
        UIRectFillUsingBlendMode(rect3, CGBlendMode.Color )
        UIRectFillUsingBlendMode(rect4, CGBlendMode.ColorBurn)
        UIRectFillUsingBlendMode(rect5, CGBlendMode.ColorDodge)
        UIRectFillUsingBlendMode(rect6, CGBlendMode.Copy)
        UIRectFillUsingBlendMode(rect7, CGBlendMode.Darken)
        UIRectFillUsingBlendMode(rect8, CGBlendMode.DestinationAtop)
        UIRectFillUsingBlendMode(rect9, CGBlendMode.DestinationIn)
        UIRectFillUsingBlendMode(rect10, CGBlendMode.DestinationOut)
        UIRectFillUsingBlendMode(rect11, CGBlendMode.DestinationOver)
        UIRectFillUsingBlendMode(rect12, CGBlendMode.Difference)
        UIRectFillUsingBlendMode(rect13, CGBlendMode.Exclusion)
        UIRectFillUsingBlendMode(rect14, CGBlendMode.HardLight)
        UIRectFillUsingBlendMode(rect15, CGBlendMode.Hue)
        UIRectFillUsingBlendMode(rect16, CGBlendMode.Lighten)

        UIRectFillUsingBlendMode(rect17, CGBlendMode.Luminosity)
        UIRectFillUsingBlendMode(rect18, CGBlendMode.Multiply)
        UIRectFillUsingBlendMode(rect19, CGBlendMode.Normal)
        UIRectFillUsingBlendMode(rect20, CGBlendMode.Overlay)
        UIRectFillUsingBlendMode(rect21, CGBlendMode.PlusDarker)
        UIRectFillUsingBlendMode(rect22, CGBlendMode.PlusLighter)
        UIRectFillUsingBlendMode(rect23, CGBlendMode.Saturation)
        UIRectFillUsingBlendMode(rect24, CGBlendMode.Screen)
        UIRectFillUsingBlendMode(rect25, CGBlendMode.SoftLight)
        UIRectFillUsingBlendMode(rect26, CGBlendMode.SourceAtop)
        UIRectFillUsingBlendMode(rect27, CGBlendMode.SourceIn)
        UIRectFillUsingBlendMode(rect28, CGBlendMode.SourceOut)
        UIRectFillUsingBlendMode(rect29, CGBlendMode.XOR)

    }
}
```

添加到一个新的vc中 效果：

![1](<https://allluckly.cn/images/blog/tuogao/tougao29/14.png>)

对比代码和显示效果查看每种叠加效果

### 上下文变换

在view中可以利用transform对试图进行平移旋转缩放，绘图中我们也经常用到图形形变，在CoreText中绘制文字的时候因为Core Graphics坐标原点在左下角、UIKit在右上角 。所有要通过变换转过来，下面通过一个图片的变换演示一下图形上下文的形变

```
class CGView8: UIView {

    override func drawRect(rect: CGRect) {
        super.drawRect(rect)

        let context = UIGraphicsGetCurrentContext()
        self.drawImage(context!)
    }
    /**
    平移旋转缩放

    - parameter context: 上下文对象
    */
    func drawImage(context:CGContextRef){
        //保存初始状态
        CGContextSaveGState(context);

        //形变第一步：图形上下文向右平移100
        CGContextTranslateCTM(context,100, 0);

        //形变第二步：缩放0.8
        CGContextScaleCTM(context, 0.8, 0.8);

        //形变第三步：旋转
        CGContextRotateCTM(context, CGFloat(M_PI_4)/4);
        let img = UIImage(named: "ddla")

        //从某一点开始绘制
        img?.drawAtPoint(CGPoint(x: 0, y: 100))

    }
}
```

![1](<https://allluckly.cn/images/blog/tuogao/tougao29/15.png>)

使用Core Graphics绘制图像

```
class CGView9: UIView {

    //    在前面基本绘图部分，绘制图像时使用了UIKit中封装的方法进行了图像绘制，我们不妨看一下使用Quartz 2D内置方法绘制是什么效果。
    override func drawRect(rect: CGRect) {
        super.drawRect(rect)

        let context = UIGraphicsGetCurrentContext()
        self.drawImage2(context!)
    }
    func drawImage2(context:CGContextRef){
        let image = UIImage(named: "ddla")

        let size = UIScreen.mainScreen().bounds.size
        CGContextSaveGState(context)
        let height:CGFloat = 450
        let y:CGFloat = 50
        //图像绘制
        let rect = CGRectMake(10, y, 300, height)

        //        在Core Graphics中坐标系的y轴正方向是向上的，坐标原点在屏幕左下角，y轴方向刚好和UIKit中y轴方向相反
        CGContextScaleCTM(context, 1.0, -1.0) //在y轴缩放-1相当于沿着x张旋转180
        CGContextTranslateCTM(context, 0, -(size.height-(size.height-2*y-height))) //向上平移
        CGContextDrawImage(context, rect, image?.CGImage)

    }

}
```

由于坐标问题 我们做了翻转和平移

![1](<https://allluckly.cn/images/blog/tuogao/tougao29/16.png>)

### 绘制到位图

利用位图图形上下文给一个图片添加水印，在下面的程序中我们首先创建上下文，然后在上下文中绘制图片、直线和文本，最后从当前位图上下文中取得最终形成的新图片显示到界面

```
class SeventhViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let image = self.drawImageAtImageContext()
        let imageView = UIImageView(image: image)
        imageView.center=CGPointMake(160, 284)

        self.view.addSubview(imageView)
    }

    func drawImageAtImageContext()->UIImage{
        // 获得一个位图图形上下文
        let size = CGSizeMake(300, 200)
        UIGraphicsBeginImageContext(size)

        let img = UIImage(named: "ddla")
        img?.drawInRect(CGRectMake(0, 0, 300, 200))//注意绘图的位置是相对于画布顶点而言，不是屏幕

        //添加水印
        let context = UIGraphicsGetCurrentContext()
        CGContextMoveToPoint(context, 200, 178)
        CGContextAddLineToPoint(context, 265, 178)

        UIColor.redColor().setStroke()
        CGContextSetLineWidth(context, 2)
        CGContextDrawPath(context, CGPathDrawingMode.Stroke)

        let str = "ZUBER"
        (str as NSString).drawInRect(CGRectMake(200, 158, 100, 30), withAttributes: [NSFontAttributeName:UIFont.boldSystemFontOfSize(18),NSForegroundColorAttributeName:UIColor.redColor()])
        //返回绘制的新图形
        let newImage = UIGraphicsGetImageFromCurrentImageContext()

        //保存图片
        //        let data = UIImagePNGRepresentation(newImage)
        //        data?.writeToFile("your_path", atomically: true)

        //不要忘记关闭对应上下文
        UIGraphicsEndImageContext()

        return newImage
    }

//    注意：上面这种方式绘制的图像除了可以显示在界面上还可以调用对应方法进行保存（代码注释中已经包含保存方法）；除此之外这种方法相比在drawRect：方法中绘制图形效率更高，它不用每次展示时都调用所有图形绘制方法。

}
```

我们直接在vc中绘制了一个image  
 结果

![1](<https://allluckly.cn/images/blog/tuogao/tougao29/17.png>)

### 绘制pdf

绘制到PDF则要启用pdf图形上下文，PDF图形上下文的创建使用方式跟位图图形上下文是类似的，需要注意的一点就是绘制内容到PDF时需要创建分页，每页内容的开始都要调用一次IGraphicsBeginPDFPage();方法。下面的示例演示了文本绘制和图片绘制（其他图形绘制也是类似的）：

```
class EighthViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        self.drawContentToPdfContext()
    }

    func drawContentToPdfContext(){

        //沙盒路径（也就是我们应用程序文件运行的路径）
        let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentationDirectory, NSSearchPathDomainMask.UserDomainMask, true)

        let path = (paths.first! as NSString).stringByAppendingPathComponent("test.pdf")
        NSFileManager.defaultManager().createFileAtPath(path, contents: nil, attributes: nil)

        //启用pdf图形上下文
        /**
        path:保存路径
        bounds:pdf文档大小，如果设置为CGRectZero则使用默认值：612*792
        pageInfo:页面设置,为nil则不设置任何信息
        */
        UIGraphicsBeginPDFContextToFile(path, CGRectZero, nil)
        //        [kCGPDFContextAuthor:"ZUBER"]
        //由于pdf文档是分页的，所以首先要创建一页画布供我们绘制
        UIGraphicsBeginPDFPage()

        let title = "开心的学习 CoreGraphics 开心的学习Swift"
        let style = NSMutableParagraphStyle()
        style.alignment = NSTextAlignment.Center
        (title as NSString).drawInRect(CGRectMake(26, 20, 300, 50) , withAttributes: [NSFontAttributeName:UIFont.boldSystemFontOfSize(18),NSParagraphStyleAttributeName:style])

        let content = "Learn about Apple products, view online manuals, get the latest downloads, and more. Connect with other Apple users, or get service, support, and professional advice from Apple."
        let style2 = NSMutableParagraphStyle()
        style2.alignment = NSTextAlignment.Left
        (content as NSString).drawInRect(CGRectMake(26, 56, 300, 255) , withAttributes: [NSFontAttributeName:UIFont.systemFontOfSize(15),NSParagraphStyleAttributeName:style2,NSForegroundColorAttributeName:UIColor.grayColor()])

        let img = UIImage(named: "ddla")
        img?.drawInRect(CGRectMake(316, 20, 290, 305))

        let img2 = UIImage(named: "ddla")
        img2?.drawInRect(CGRectMake(6, 320, 600, 281))

        //创建新的一页继续绘制
        UIGraphicsBeginPDFPage()
        let img3 = UIImage(named: "ddla")
        img3?.drawInRect(CGRectMake(6, 20, 600, 629))

        //结束pdf上下文
        UIGraphicsEndPDFContext()

        let scrollView = UIScrollView()
        scrollView.frame = CGRectMake(0,64,self.view.frame.width,self.view.frame.height-64)
        scrollView.contentSize = CGSizeMake(self.view.frame.width+200,800)
        //把pdf显示在view上
        let ctView = CGView10(frame:CGRectMake(0,64,self.view.frame.width+200,800))
        ctView.path = path
        scrollView.addSubview(ctView)
        self.view.addSubview(scrollView)
        ctView.backgroundColor = UIColor.whiteColor()

    }

}
```

我们先拿到的一个目录添加了我们的文件名 然后创建了文件可以绘制 。最后CGView10读取了一页

```
import UIKit

class CGView10: UIView {

var path:String!

    override func drawRect(rect: CGRect) {
        super.drawRect(rect)

        let context = UIGraphicsGetCurrentContext()
        CGContextTranslateCTM(context, 0.0, self.bounds.size.height)
        CGContextScaleCTM(context, 1.0, -1.0)
        let url = NSURL(fileURLWithPath: path) as CFURLRef
        let pdf = CGPDFDocumentCreateWithURL(url)
        let page = CGPDFDocumentGetPage(pdf, 1)
        CGContextSaveGState(context)

        //        let pdfTransform = CGPDFPageGetDrawingTransform(page, CGPDFBox.CropBox, self.bounds, 0, true)
        //        CGContextConcatCTM(context, pdfTransform)

        CGContextDrawPDFPage(context, page);
        CGContextRestoreGState(context);
    }
}
```

效果

![1](<https://allluckly.cn/images/blog/tuogao/tougao29/18.png>)

drawRect的刷新直接调用setNeedsLayout相信大家都知道在适当的时候调用即可。  
 如果想做画板应用这个应该会经常使用。  
 [github地址:](<https://github.com/smalldu/ZZCoreGraphics>)

参考链接：[参考](<http://www.cnblogs.com/kenshincui/p/3959951.html>)

---

好文推荐：[Bison眼中的iOS开发多线程是这样的（三）](<https://allluckly.cn/%E5%A4%9A%E7%BA%BF%E7%A8%8B/duoxiancheng03>)

