iOS中CAShapeLayer的介绍和使用

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
}
屏幕快照 2018-11-26 下午2.56.32.png
  • 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
}
屏幕快照 2018-11-26 下午2.56.38.png

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:是否是顺时针方向

屏幕快照 2018-11-26 下午3.46.00.png
  • 根据路径绘制
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:控制点

屏幕快照 2018-11-26 下午3.45.18.png

三次贝塞尔曲线

endPoint:贝塞尔曲线终点
controlPoint1:控制点1
controlPoint2:控制点2

屏幕快照 2018-11-26 下午3.45.43.png

实战

绘制矩形

  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)
    }
屏幕快照 2018-11-26 下午2.37.43.png

可以看到我们仅仅是创建了一个路径,然后使用CAShapeLayer进行绘制,很容易就显示矩形了,因为默认的填充颜色为黑色,所以看到的是黑色的矩形。

  • 修改填充颜色(fillColor)
shapeLayer.fillColor = UIColor.red.cgColor
屏幕快照 2018-11-26 下午2.40.27.png
  • 设置描边颜色(strokeColor)
shapeLayer.strokeColor = UIColor.black.cgColor
屏幕快照 2018-11-26 下午2.41.48.png

默认为1的边线宽度

  • 设置线宽
shapeLayer.lineWidth = 5
屏幕快照 2018-11-26 下午2.43.25.png
  • 拐点样式(lineJoin)
shapeLayer.lineWidth = 20 // 增大边框,方便观察
shapeLayer.lineJoin = kCALineJoinRound
屏幕快照 2018-11-26 下午3.13.56.png
  • 描边开始和结束位置(strokeStart和strokeEnd)
shapeLayer.strokeStart = 0.2
shapeLayer.strokeEnd = 0.95
屏幕快照 2018-11-26 下午3.09.26.png
  • 线模版(lineDashPattern)
 shapeLayer.lineDashPattern = [5,2,8,3]
屏幕快照 2018-11-26 下午4.02.47.png

这句话的意思是说这个虚线由四部分组成:

  • 第一段实线长度为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)
 }
屏幕快照 2018-11-26 下午3.18.55.png

绘制花朵

  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)
    }
屏幕快照 2018-11-26 下午3.24.11.png

进度条动画

  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)
    }
2018-11-26 16-51-01.2018-11-26 16_51_15.gif

参考

CAShapeLayer

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

推荐阅读更多精彩内容