CAShapeLayer是图形layer层,我们可以自定义这个层的形状。CAShapeLayer是一个通过矢量图形而不是bitmap来绘制的图层子类。指定诸如颜色和线宽等属性,用CGPath来定义想要绘制的图形,最后CAShapeLayer就自动渲染出来了。当然,也可以用Core Graphics直接向原始的CALyer的内容中绘制一个路径,相比之下,使用CAShapeLayer有以下一些优点:
- 渲染快速。CAShapeLayer使用了硬件加速,绘制同一图形会比用Core Graphics快很多。
- 高效使用内存。一个CAShapeLayer不需要像普通CALayer一样创建一个寄宿图形,所以无论有多大,都不会占用太多的内存。
- 不会被图层边界剪裁掉。一个CAShapeLayer可以在边界之外绘制。图层路径不会像在使用Core Graphics的普通CALayer一样被剪裁掉。
- 不会出现像素化。当把CAShapeLayer放大,或是用3D透视变换将其离相机更近时,它不像一个有寄宿图的普通图层一样变得像素化。
CAShapeLayer可以用来绘制所有能够通过CGPath来表示的形状。这个形状不一定要闭合,图层路径也不一定要不间断的,事实上可以在一个图层上绘制好几个不同的形状。
属性
- 指定图像的路径(Specifying the Shape Path)
// 路径
open var path: CGPath?
- 样式属性(Shape Style Properties)
// 路径填充颜色,默认黑色,可动画属性
open var fillColor: CGColor?
// 路径填充规则,奇偶或者非零,默认是非零
open var fillRule: CAShapeLayerFillRule
// 描边颜色,默认为nil,可动画属性
open var strokeColor: CGColor?
// 描边的起点和终点
open var strokeStart: CGFloat // 它表示描线开始的地方占总路径的百分比
open var strokeEnd: CGFloat // 表示绘制结束的地方站总路径的百分比
// 描边的宽,默认为1,可动画属性
open var lineWidth: CGFloat
// 最大斜街长度
open var miterLimit: CGFloat
// 线端点的样式
open var lineCap: CAShapeLayerLineCap
// 线拐点的样式
open var lineJoin: CAShapeLayerLineJoin
// 边线模版的起点
open var lineDashPhase: CGFloat
// 设置边线的样式,默认为实线
open var lineDashPattern: [NSNumber]?
- lineDashPattern
这是一个NSNumber的数组,索引从1开始记,奇数位数值表示实线长度,偶数位数值表示空白长度。系统会按照数值自动重复设置虚线。
- miterLimit
最大斜接长度。斜接长度指的是在两条线交汇处和外交之间的距离。只有lineJoin属性为kCALineJoinMiter时miterLimit才有效。边角的角度越小,斜接长度就会越大。为了避免斜接长度过长,我们可以使用miterLimit属性。如果斜接长度超过miterLimit的值,边角会以lineJoin的“bevel”即kCALineJoinBevel类型来显示
- CAShapeLayerLineJoin
extension CAShapeLayerLineJoin {
@available(iOS 3.0, *)
public static let miter: CAShapeLayerLineJoin
@available(iOS 3.0, *)
public static let round: CAShapeLayerLineJoin
@available(iOS 3.0, *)
public static let bevel: CAShapeLayerLineJoin
}
- CAShapeLayerLineCap
extension CAShapeLayerLineCap {
@available(iOS 3.0, *)
public static let butt: CAShapeLayerLineCap
@available(iOS 3.0, *)
public static let round: CAShapeLayerLineCap
@available(iOS 3.0, *)
public static let square: CAShapeLayerLineCap
}
UIBezierPath
UIBezierPath 专门是用来绘制路径的,常和CAShapeLayer一起配合使用
- 绘制矩形
public convenience init(rect: CGRect)
- 绘制椭圆形
public convenience init(ovalIn rect: CGRect)
- 绘制圆角
public convenience init(roundedRect rect: CGRect, cornerRadius: CGFloat) // rounds all corners with the same horizontal and vertical radius
public convenience init(roundedRect rect: CGRect, byRoundingCorners corners: UIRectCorner, cornerRadii: CGSize)
- 绘制圆弧
public convenience init(arcCenter center: CGPoint,
radius: CGFloat,
startAngle: CGFloat,
endAngle: CGFloat,
clockwise: Bool)
open func addArc(withCenter center: CGPoint,
radius: CGFloat,
startAngle: CGFloat,
endAngle: CGFloat,
clockwise: Bool)
center:圆弧的中心
radius:圆弧半角
startAngle:起始点的角度(相对坐标系0)
endAngle:结束点的角度
endAngle:是否是顺时针方向
- 根据路径绘制
public convenience init(cgPath CGPath: CGPath)
- 绘制线段
// 添加路径起点
open func move(to point: CGPoint)
// 添加直线到另外一个点
open func addLine(to point: CGPoint)
- 绘制贝塞尔曲线
// 三次贝塞尔曲线
open func addCurve(to endPoint: CGPoint, controlPoint1: CGPoint, controlPoint2: CGPoint)
// 二次贝塞尔曲线
open func addQuadCurve(to endPoint: CGPoint, controlPoint: CGPoint)
二次贝塞尔曲线
endPoint:贝塞尔曲线终点
controlPoint:控制点
三次贝塞尔曲线
endPoint:贝塞尔曲线终点
controlPoint1:控制点1
controlPoint2:控制点2
实战
绘制矩形
func drawRect() {
let path = UIBezierPath(rect: CGRect(x: 100, y: 100, width: 100, height: 100))
let shapeLayer = CAShapeLayer()
// 设置路径
shapeLayer.path = path.cgPath
view.layer.addSublayer(shapeLayer)
}
可以看到我们仅仅是创建了一个路径,然后使用CAShapeLayer进行绘制,很容易就显示矩形了,因为默认的填充颜色为黑色,所以看到的是黑色的矩形。
- 修改填充颜色(fillColor)
shapeLayer.fillColor = UIColor.red.cgColor
- 设置描边颜色(strokeColor)
shapeLayer.strokeColor = UIColor.black.cgColor
默认为1的边线宽度
- 设置线宽
shapeLayer.lineWidth = 5
- 拐点样式(lineJoin)
shapeLayer.lineWidth = 20 // 增大边框,方便观察
shapeLayer.lineJoin = kCALineJoinRound
- 描边开始和结束位置(strokeStart和strokeEnd)
shapeLayer.strokeStart = 0.2
shapeLayer.strokeEnd = 0.95
- 线模版(lineDashPattern)
shapeLayer.lineDashPattern = [5,2,8,3]
这句话的意思是说这个虚线由四部分组成:
- 第一段实线长度为5
- 画完长度为5像素的实线之后,空2像素
- 空完2像素之后,再画8像素的实线
- 画完长度为8像素的实线之后,空3像素
- 然后重复这个数组中的数值,一直不停的绘画。
绘制圆形
func drawARC() {
let shapeLayer = CAShapeLayer()
shapeLayer.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
shapeLayer.fillColor = UIColor.orange.cgColor
shapeLayer.lineWidth = 3
shapeLayer.strokeColor = UIColor.black.cgColor
// 画弧
let path = UIBezierPath(arcCenter: shapeLayer.position,
radius: 100,
startAngle: 0,
endAngle: CGFloat(2 * Float.pi),
clockwise: true)
shapeLayer.path = path.cgPath
// 设置居中
shapeLayer.position = view.center
view.layer.addSublayer(shapeLayer)
}
绘制花朵
func drawFlower() {
// 创建shapeLayer
let shapeLayer = CAShapeLayer()
shapeLayer.position = view.center
// 创建一个可变路径
let path = CGMutablePath()
stride(from: 0, to: CGFloat.pi * 2, by: CGFloat.pi / 6).forEach {
angle in
var transform = CGAffineTransform(rotationAngle: angle)
// 绘制椭圆路径
let petal = CGPath(ellipseIn: CGRect(x: -20, y: 0, width: 40, height: 100),
transform: &transform)
path.addPath(petal)
}
// 指定绘制的路径
shapeLayer.path = path
// 边框颜色
shapeLayer.strokeColor = UIColor.red.cgColor
// 路径的填充颜色
shapeLayer.fillColor = UIColor.yellow.cgColor
// 填充规则
shapeLayer.fillRule = kCAFillRuleEvenOdd
view.layer.addSublayer(shapeLayer)
}
进度条动画
func ovalAnimation() {
// 创建CAShapeLayer
let shapeLayer = CAShapeLayer()
shapeLayer.bounds = CGRect(x: 0, y: 0, width: 100, height: 100)
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.lineWidth = 2.0
// 添加路径
let path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 100, height: 100))
shapeLayer.path = path.cgPath
shapeLayer.position = view.center
view.layer.addSublayer(shapeLayer)
// 添加动画
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.duration = 2.0
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
animation.fromValue = 0.0
animation.toValue = 1.0
animation.fillMode = kCAFillModeForwards
animation.isRemovedOnCompletion = false
shapeLayer.add(animation, forKey: nil)
}
参考
CAShapeLayer