当前位置: > > > Swift - Core Graphics绘图框架详解1(绘制线条)

Swift - Core Graphics绘图框架详解1(绘制线条)

一、Core Graphics介绍

1,什么是Core Graphics

(1)Core Graphics Framework 是一套基于 CAPI 框架,使用了 Quartz 作为绘图引擎,可用于一切绘图操作。它提供了低级别、轻量级、高保真度的 2D 渲染。
(2)Quartz 2D Core Graphics Framework 的一部分,是一个强大的二维图像绘制引擎。
(3)我们使用的 UIKit 库中所有 UI 组件其实都是由 CoreGraphics 绘制实现的。所以使用 Core Graphics 可以实现比 UIKit 更底层的功能。
(4)当我们引入 UIKit 框架时系统会自动引入 Core Graphics 框架,同时在 UIKit 内部还对一些常用的绘图 API 进行了封装,方便我们使用。 (比如:CGMutablePathCore Graphics 的底层API,而 UIBezierPath 就是对 CGMutablePath 的封装。)

2,绘图的一般步骤

(1)获取绘图上下文
(2)创建并设置路径
(3)将路径添加到上下文
(4)设置上下文状态(如笔触颜色、宽度、填充色等等)
(5)绘制路径

二、线条的绘制

1,绘制直线

import UIKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let frame = CGRect(x: 30, y: 30, width: 250, height: 100)
        let cgView = CGView(frame: frame)
        self.view.addSubview(cgView)
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

class CGView:UIView {
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        //设置背景色为透明,否则是黑色背景
        self.backgroundColor = UIColor.clear
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func draw(_ rect: CGRect) {
        super.draw(rect)
        
        //获取绘图上下文
        guard let context = UIGraphicsGetCurrentContext() else {
            return
        }
        
        //创建一个矩形,它的所有边都内缩3
        let drawingRect = self.bounds.insetBy(dx: 3, dy: 3)
        
        //创建并设置路径
        let path = CGMutablePath()
        path.move(to: CGPoint(x:drawingRect.minX, y:drawingRect.minY))
        path.addLine(to:CGPoint(x:drawingRect.maxX, y:drawingRect.minY))
        path.addLine(to:CGPoint(x:drawingRect.maxX, y:drawingRect.maxY))
        
        //添加路径到图形上下文
        context.addPath(path)
        
        //设置笔触颜色
        context.setStrokeColor(UIColor.orange.cgColor)
        //设置笔触宽度
        context.setLineWidth(6)
        
        //绘制路径
        context.strokePath()
    }
}

2,设置端点的样式 

通过 setLineCap 可以设置线条端点(顶点)的样式,使用的是 CGLineCap 枚举,内容如下:
  • .butt:不绘制端点, 线条结尾处直接结束。(默认值)
  • .round:绘制圆形端点, 线条结尾处绘制一个直径为线条宽度的半圆
  • .square:线条结尾处绘制半个边长为线条宽度的正方形。这种形状的端点与“butt”形状的端点十分相似,只是线条略长一点而已。
下面样例使用圆形端点:
//设置顶点样式(圆角)
context.setLineCap(.round)

3,设置连接点的样式 

通过 setLineJoin 可以设置线条拐点的样式,使用的是 CGLineJoin 枚举,内容如下:
  • .mitre:锐角斜切(默认值)
  • .round:圆头
  • .bevel:平头斜切
下面样例使用圆头连接点:
//设置连接点样式(圆角)
context.setLineJoin(CGLineJoin.round)

4,设置阴影

我们可以设置阴影的偏移量、模糊度和颜色。 
//设置阴影
context.setShadow(offset: CGSize(width:3, height:3), blur: 0.6,
                  color: UIColor.lightGray.cgColor)

5,绘制虚线

class CGView:UIView {
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        //设置背景色为透明,否则是黑色背景
        self.backgroundColor = UIColor.clear
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func draw(_ rect: CGRect) {
        super.draw(rect)
        
        //获取绘图上下文
        guard let context = UIGraphicsGetCurrentContext() else {
            return
        }
        
        //创建一个矩形,它的所有边都内缩3
        let drawingRect = self.bounds.insetBy(dx: 3, dy: 3)
        
        //创建并设置路径
        let path = CGMutablePath()
        path.move(to: CGPoint(x:drawingRect.minX, y:drawingRect.minY))
        path.addLine(to:CGPoint(x:drawingRect.maxX, y:drawingRect.minY))
        path.addLine(to:CGPoint(x:drawingRect.maxX, y:drawingRect.maxY))
        
        //添加路径到图形上下文
        context.addPath(path)
        
        //设置笔触颜色
        context.setStrokeColor(UIColor.orange.cgColor)
        //设置笔触宽度
        context.setLineWidth(6)
        
        //虚线每个线段的长度与间隔
        let lengths: [CGFloat] = [15,4]
        //设置虚线样式
        context.setLineDash(phase: 0, lengths: lengths)
        
        //绘制路径
        context.strokePath()
    }
}
当然我们也可以设置虚线的顶点和连接点的样式(这里我都使用圆形):

6,绘制圆弧

圆弧就是圆上的一部分。要绘制圆弧必须确定:圆形中点的坐标、圆的半径、圆弧的起点角度和终点角度。
其中起点角度和终点角度都要用弧度表示,即常量 pi 的倍数(1pi 是半圆,2pi 是整个圆形)。
下面样例中,我们在自定义的视图中心位置绘制一段 3/4 的圆弧,半径根据这个视图的尺寸来定。  
class CGView:UIView {
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        //设置背景色为透明,否则是黑色背景
        self.backgroundColor = UIColor.clear
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func draw(_ rect: CGRect) {
        super.draw(rect)
        
        //获取绘图上下文
        guard let context = UIGraphicsGetCurrentContext() else {
            return
        }
        
        //创建一个矩形,它的所有边都内缩3
        let drawingRect = self.bounds.insetBy(dx: 3, dy: 3)
        
        //创建并设置路径
        let path = CGMutablePath()
        
        //圆弧半径
        let radius = min(drawingRect.width, drawingRect.height)/2
        //圆弧中点
        let center = CGPoint(x:drawingRect.midX, y:drawingRect.midY)
        //绘制圆弧
        path.addArc(center: center, radius: radius, startAngle: 0,
                    endAngle: CGFloat.pi * 1.5, clockwise: false)
        
        //添加路径到图形上下文
        context.addPath(path)
        
        //设置笔触颜色
        context.setStrokeColor(UIColor.orange.cgColor)
        //设置笔触宽度
        context.setLineWidth(6)
        
        //绘制路径
        context.strokePath()
    }
}

7,贝塞尔曲线绘制

Quartz 2D 中曲线绘制分为:二次贝塞尔曲线和三次贝塞尔曲线。二次曲线只有一个控制点,而三次曲线有两个控制点。
(1)二次贝塞尔曲线绘制
class CGView:UIView {
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        //设置背景色为透明,否则是黑色背景
        self.backgroundColor = UIColor.clear
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func draw(_ rect: CGRect) {
        super.draw(rect)
        
        //获取绘图上下文
        guard let context = UIGraphicsGetCurrentContext() else {
            return
        }
        
        //创建一个矩形,它的所有边都内缩3点
        let drawingRect = self.bounds.insetBy(dx: 3, dy: 3)
        
        //创建并设置路径
        let path = CGMutablePath()
        //移动起点
        path.move(to: CGPoint(x: drawingRect.minX, y: drawingRect.maxY))
        //二次贝塞尔曲线终点
        let toPoint = CGPoint(x: drawingRect.maxX, y: drawingRect.maxY)
        //二次贝塞尔曲线控制点
        let controlPoint = CGPoint(x: drawingRect.midX, y: drawingRect.minY)
        //绘制二次贝塞尔曲线
        path.addQuadCurve(to: toPoint, control: controlPoint)
        
        //添加路径到图形上下文
        context.addPath(path)
        
        //设置笔触颜色
        context.setStrokeColor(UIColor.orange.cgColor)
        //设置笔触宽度
        context.setLineWidth(6)
        
        //绘制路径
        context.strokePath()
    }
}

(2)三次贝塞尔曲线绘制
class CGView:UIView {
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        //设置背景色为透明,否则是黑色背景
        self.backgroundColor = UIColor.clear
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func draw(_ rect: CGRect) {
        super.draw(rect)
        
        //获取绘图上下文
        guard let context = UIGraphicsGetCurrentContext() else {
            return
        }
        
        //创建一个矩形,它的所有边都内缩3
        let drawingRect = self.bounds.insetBy(dx: 3, dy: 3)
        
        //创建并设置路径
        let path = CGMutablePath()
        //移动起点
        path.move(to: CGPoint(x: drawingRect.minX, y: drawingRect.maxY))
        //三次贝塞尔曲线终点
        let toPoint = CGPoint(x: drawingRect.maxX, y: drawingRect.minY)
        //三次贝塞尔曲线第1个控制点
        let controlPoint1 = CGPoint(x: (drawingRect.minX+drawingRect.midX)/2, y: drawingRect.minY)
        //三次贝塞尔曲线第2个控制点
        let controlPoint2 = CGPoint(x: (drawingRect.midX+drawingRect.maxX)/2, y: drawingRect.maxY)
        //绘制三次贝塞尔曲线
        path.addCurve(to: toPoint, control1: controlPoint1, control2: controlPoint2)
        
        //添加路径到图形上下文
        context.addPath(path)
        
        //设置笔触颜色
        context.setStrokeColor(UIColor.orange.cgColor)
        //设置笔触宽度
        context.setLineWidth(6)
        
        //绘制路径
        context.strokePath()
    }
}
评论9
  • 9楼
    2017-09-29 16:43
    bird

    hangge,我已经绘制好了一个view,当我想改变绘制的内容,也就是情况原有的view,为该view重新绘制内容,该怎么做?

    站长回复

    要动态改变view里绘制的内容,这样参考我之前写的这篇文章:Swift - 制作一个带动画效果的环形进度条。 它们的原理都差不多的。

  • 8楼
    2017-09-29 15:08
    yzstyle

    Error: this application, or a library it uses, has passed an invalid numeric value (NaN, or not-a-number) to CoreGraphics API and this value is being ignored. Please fix this problem.
    If you want to see the backtrace, please set CG_NUMERICS_SHOW_BACKTRACE environmental variable.
    没有显示出来,然后就打印这两句出来,也没有崩溃

    站长回复

    运行的是我文章中的代码吗?看样子像是参数传递有误,你再检查下。

  • 7楼
    2017-09-03 15:45
    hh

    看你的东西很受益,谢谢你的贡献!
    我想问问我想画一条直线,已知两个点的坐标但是没有画出来,不知道是什么原因,下面是代码,请指教一下:
    import UIKit

    class ViewController: UIViewController {
    override func viewDidLoad() {
    super.viewDidLoad()
    let drawline = DrawLine()
    self.view.addSubview(drawline)
    }

    override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    }
    }

    class DrawLine:UIView {
    let lineW:CGFloat = 1
    let startP:CGPoint = CGPoint(x:CGFloat(50),y:CGFloat(50))
    let endP:CGPoint = CGPoint(x:CGFloat(150),y:CGFloat(150))

    override func draw(_ rect: CGRect) {
    let context = UIGraphicsGetCurrentContext()
    context?.setLineCap(.round)
    context?.setLineJoin(.round)
    context?.beginPath()
    context?.move(to: startP)
    context?.addLine(to: endP)
    context?.setLineWidth(lineW)
    context?.strokePath()
    }
    }

    站长回复

    (1)你没有给drawline设置位置和尺寸:

    let frame = CGRect(x: 30, y: 30, width: 250, height: 300)
    let drawline = DrawLine(frame: frame)

    (2)还有记得把drawline背景色为透明,否则是黑色背景(可以参考文章里的代码)

  • 6楼
    2017-08-16 17:01
    CSXSky

    感谢你的分享,真干货,给了我很大帮助

    站长回复

    不客气,能帮助到你我也很高兴。

  • 5楼
    2017-07-31 14:14
    iOS

    请问有木有
    //移动起点
    path.move(to: CGPoint(x: drawingRect.minX, y: drawingRect.maxY))
    //二次贝塞尔曲线终点
    let toPoint = CGPoint(x: drawingRect.maxX, y: drawingRect.maxY)
    //二次贝塞尔曲线控制点
    let controlPoint = CGPoint(x: drawingRect.midX, y: drawingRect.minY)
    这里这些数组变化的详细文章呀?

    站长回复

    我觉得这个已经就是关于Core Graphics绘图详细的文章了啊。

  • 4楼
    2017-07-31 09:28
    pheromone

    学习了.谢谢

    站长回复

    不客气。

  • 3楼
    2017-07-30 10:17
    小莊

    文章很棒!
    對CG得理解很淺,只能大略看懂,能有這種圖文並茂又用Swift3實現的文章,對理解上,幫了大忙!
    十分感謝!

    站长回复

    不客气。我的文章能对你有所帮助,我也很高兴。

  • 2楼
    2017-03-03 17:36
    kkk

    十分感谢,受益匪浅。一直找不到 swift 的 cg教程,找到的话也不是 swift3的,十分感谢航歌。

    站长回复

    不客气,我的文章能帮助到你,我也很高兴。

  • 1楼
    2017-02-09 18:00

    为什么我写这个就报错
    //创建并设置路径 let path = CGMutablePath()
    只能用CGPathCreateMutable() ?用上面的就报错,说没有初始化那意思

    站长回复

    我测试了下是没问题的啊,是不是你Xcode版本太低,升级成最新的试试。