UIBeizerPath简介
A path that consists of straight and curved line segments that you can render in your custom views.
UIBezierPath(贝塞尔曲线),位于 UIKit 框架,使用此类可以定义简单的形状,如椭圆或者矩形,或者有多个直线和曲线段组成的形状.我们可以使用直线段去创建矩形和多边形,使用曲线段去创建弧(arc),圆或者其他复杂的曲线形状. 曲线定义: 起始点,终止点(锚点),控制点.通过调整控制点,贝塞尔曲线会发生变化.
初始化 UIBezierPath
系统提供了多种初始化 UIBezierPath 的方法:
// 返回一个矩形 path
public convenience init(rect: CGRect)
// 返回一个圆形或者椭圆形 path
public convenience init(ovalIn rect: CGRect)
// 返回一个带圆角的矩形 path ,矩形的四个角都是圆角;
public convenience init(roundedRect rect: CGRect, cornerRadius: CGFloat)
// 返回一个带圆角的矩形 path , UIRectCorner 指定绘制哪些圆角;
public convenience init(roundedRect rect: CGRect, byRoundingCorners corners: UIRectCorner, cornerRadii: CGSize)
// 返回一段圆弧,参数说明: center: 弧线中心点的坐标 radius: 弧线所在圆的半径 startAngle: 弧线开始的角度值 endAngle: 弧线结束的角度值 clockwise: 是否顺时针画弧线.
public convenience init(arcCenter center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool)
// 用一条 CGpath 初始化
public convenience init(cgPath CGPath: CGPath)
// 初始化方法,需要用实例方法添加线条.使用比较多,可以根据需要任意定制样式,画任何我们想画的图形.
public init()
// 返回一个反转当前路径的路径对象.(反方向绘制path)
open func reversing() -> UIBezierPath // Modified paths
创建 UIBezierPath
1. 矩形 Path
let path = UIBezierPath(rect: CGRect(x: 100, y: 400, width: 100, height: 50))
path.lineWidth = 2 // 线条宽度
path.stroke()
2. 椭圆 path
// 宽高相等为圆, 不等则为椭圆
let path = UIBezierPath(ovalIn: CGRect(x: 100, y: 400, width: 100, height: 100))
path.lineWidth = 2
path.stroke()
3. 圆角矩形 path
let path = UIBezierPath(roundedRect: CGRect(x: 100, y: 400, width: 100, height: 80), cornerRadius: 20)
path.lineWidth = 2
path.stroke()
4. 指定圆角的矩形 path
let path = UIBezierPath(roundedRect: CGRect(x: 100, y: 400, width: 100, height: 80), byRoundingCorners: [.bottomLeft, .topRight], cornerRadii: CGSize(width: 20, height: 20))
path.lineWidth = 2
path.stroke()
5. 一段圆弧 path
let path = UIBezierPath(arcCenter: CGPoint(x: 150, y: 400), radius: 50, startAngle: 0, endAngle: CGFloat.pi * 0.5, clockwise: false)
path.lineWidth = 2
path.stroke()
其中角度示意图如下:

6. 自定义 path
let path = UIBezierPath()
path.lineWidth = 2
let center = CGPoint(x: 150, y: 400)
path.move(to: center)
path.addArc(withCenter: center, radius: 50, startAngle: CGFloat.pi / 6.0, endAngle: CGFloat.pi * 11 / 6.0, clockwise: true)
path.close()
path.stroke()
构建 Path
上面前 5 种,使用了系统方法创建 path,第 6 种则自定义了简单的 path,创建自定义 path, 会用到以下方法:
// 以 point点 开始作为起点, 一般用`UIBezierPath()`创建的贝塞尔曲线,先用该方法标注一个起点,再调用其他的创建线条的方法来绘制曲线
open func move(to point: CGPoint)
// 绘制二次贝塞尔曲线的关键方法,即从path的最后一点开始添加一条线到point点
open func addLine(to point: CGPoint)
e.g. 
let path = UIBezierPath()
path.lineWidth = 2
path.move(to: CGPoint(x: 100, y: 400))
path.addLine(to: CGPoint(x: 200, y: 400))
path.stroke()
// 绘制二次贝塞尔曲线的关键方法,和`-moveToPoint:`配合使用. endPoint为终止点,controlPoint为控制点.
open func addQuadCurve(to endPoint: CGPoint, controlPoint: CGPoint)
e.g. 
let path = UIBezierPath()
path.lineWidth = 2
path.move(to: CGPoint(x: 100, y: 400))
path.addLine(to: CGPoint(x: 200, y: 400))
path.addQuadCurve(to: CGPoint(x: 150, y: 600), controlPoint: CGPoint(x: 150, y: 450))
path.stroke()
// 绘制三次贝塞尔曲线的关键方法,以三个点画一段曲线. 一般和moveToPoint:配合使用.
// 其中,起始点由`-moveToPoint:`设置,终止点位为`endPoint:`, 控制点1的坐标controlPoint1,控制点2的坐标是controlPoint2.
open func addCurve(to endPoint: CGPoint, controlPoint1: CGPoint, controlPoint2: CGPoint)
e.g. 
let path = UIBezierPath()
path.lineWidth = 2
path.move(to: CGPoint(x: 230, y: 140))
path.addCurve(to: CGPoint(x: 230, y: 415), controlPoint1: CGPoint(x: 145, y: 275), controlPoint2: CGPoint(x: 340, y: 275))
path.stroke()
// 闭合路径
open func close()
// 移除所有的点,从而有效地删除所有子路径
open func removeAllPoints()
// 追加 Path 到路径
open func append(_ bezierPath: UIBezierPath)
// 用仿射变换矩阵变换路径的所有点
open func apply(_ transform: CGAffineTransform)
path 信息
// A Boolean value indicating whether the path has any valid elements.
// 该值指示路径是否有任何有效的元素
open var isEmpty: Bool { get }
// The bounding rectangle of the path.
// 路径包括的矩形
open var bounds: CGRect { get }
// The current point in the graphics path.
// 图形路径中的当前点
open var currentPoint: CGPoint { get }
// Returns a Boolean value indicating whether the area enclosed by the receiver contains the specified point.
// 接收器是否包含指定的点
open func contains(_ point: CGPoint) -> Bool
绘制属性
// The line width of the path.
// 线宽
open var lineWidth: CGFloat
// The shape of the paths end points when stroked.
// 绘制时端点类型
// .butt
// .round 圆角
// .suqare 方形,样式上和 .butt是一样的,但是比 .butt 长一点
open var lineCapStyle: CGLineCap
// The shape of the joints between connected segments of a stroked path.
// 线段连接类型
// .miter 尖角
// .round 圆角
// .bevel 斜角
open var lineJoinStyle: CGLineJoin
// The limiting value that helps avoid spikes at junctions between connected line segments
// 最大斜接长度(只有在使用kCGLineJoinMiter是才有效,最大限制为10), 边角的角度越小,斜接长度就会越大,为了避免斜接长度过长,使用lineLimit属性限制,如果斜接长度超过miterLimit,边角就会以KCALineJoinBevel类型来显示
open var miterLimit: CGFloat // Used when lineJoinStyle is kCGLineJoinMiter
// The factor that determines the rendering accuracy for curved path segments.
// 决定曲线路径段渲染精度
open var flatness: CGFloat
// A Boolean indicating whether the even-odd winding rule is in use for drawing paths.
// 判断奇偶数组的规则绘制图像,图形复杂时填充颜色的一种规则。类似棋盘
// Default is NO. When YES, the even-odd fill rule is used for drawing, clipping, and hit testing.
open var usesEvenOddFillRule: Bool
// Sets the line-stroking pattern for the path.
// 设置路径的线条描边模式
// 为path绘制虚线,dash数组存放各段虚线的长度,count是数组元素数量,phase是起始位置
open func setLineDash(_ pattern: UnsafePointer<CGFloat>?, count: Int, phase: CGFloat)
open func getLineDash(_ pattern: UnsafeMutablePointer<CGFloat>?, count: UnsafeMutablePointer<Int>?, phase: UnsafeMutablePointer<CGFloat>?)
图形上下文中的路径操作
// Paints the region enclosed by the receiver’s path using the current drawing properties.
// 填充路径闭合的区域
func fill()
// Paints the region enclosed by the receiver’s path using the specified blend mode and transparency values.
// 填充模式, alpha 设置
// blendMode : https://onevcat.com/2013/04/using-blending-in-ios/
func fill(with: CGBlendMode, alpha: CGFloat)
// Draws a line along the receiver’s path using the current drawing properties.
各个点连线
func stroke()
// Draws a line along the receiver’s path using the specified blend mode and transparency values.
// 链接模式, alpha 设置
func stroke(with: CGBlendMode, alpha: CGFloat)
// Intersects the area enclosed by the receiver’s path with the clipping path of the current graphics context and makes the resulting shape the current clipping path.
// 图形绘制超出当前路径范围,则不可见
func addClip()
显示到屏幕上
UIBezierPath 生成的是矢量图形,不能直接显示在屏幕上,如果想让其展示在屏幕上,有两种方法:
- 在 UIView 的
func draw(_ rect: CGRect)中绘制,并设置颜色
override func draw(_ rect: CGRect) {
UIColor.orange.set()
let path = UIBezierPath()
path.lineWidth = 2
path.move(to: CGPoint(x: 230, y: 140))
path.addCurve(to: CGPoint(x: 230, y: 415), controlPoint1: CGPoint(x: 145, y: 275), controlPoint2: CGPoint(x: 340, y: 275))
path.stroke()
}
2.将 一个 layer 的 path 指向 UIBezierPath 实例的 cgPath:
let path = UIBezierPath(roundedRect: CGRect(x: 100, y: 100, width: 100, height: 100), cornerRadius: 50)
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.fillColor = UIColor.orange.cgColor
shapeLayer.strokeColor = UIColor.green.cgColor
view.layer.addSublayer(shapeLayer)
应用
给 UIView 添加圆角,以 UIImageView 为例
let path = UIBezierPath(roundedRect: imageView.bounds, cornerRadius: 0)
let round = UIBezierPath(arcCenter: CGPoint(x: imageView.bounds.width / 2, y: imageView.bounds.height / 2), radius: imageView.bounds.width / 2, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: false)
path.append(round) // path 包含的区域如下图红色部分
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.fillColor = UIColor.red.cgColor
imageView.layer.borderColor = UIColor.clear.cgColor
imageView.layer.addSublayer(shapeLayer)
要达到圆角效果需要将 fillColor 和 imageView 的 superView 的颜色保持一致, 即:
shapeLayer.fillColor = UIColor.red.cgColor
最终效果如下: