iOS CALayer专用图层理解与使用方法一(CAShapeLayer、CATextLayer、CATransformLayer)

1. 概述

本专栏之前的文章讲述了CALayer的使用以及一些动画操作等,本篇文章主要对CALayer的一些专用图层CAShapeLayer、CATextLayer、CATransformLayer进行讲解。

2. CAShapeLayer

A layer that draws a cubic Bezier spline in its coordinate space.

class CAShapeLayer : CALayer

CAShapeLayer是一个通过矢量图形而不是bitmap来绘制的图层子类,我们可以指定线宽、颜色等属性,用CGPath来绘制想要的图层,最后CAShapeLayer就自动渲染出来了。除了这种方法,我们也可以通过Core Graphics向原始Layer直接绘制一个路径,不过相比之下,CAShapeLayer有以下有点:

  • 渲染速度快。CAShapeLayer使用了硬件加速,绘制同一个图形会比Core Graphics快很多。
  • 高效使用内存。一个CAShapeLayer不需要像普通的Layer一样创建一个寄宿图形,所以无论多大,都不会占用太多的内存。
  • 不会被图层边界裁剪。 一个 CAShapeLayer 可以在边界之外绘制。
  • 不会出现像素化。当给 CAShapeLayer 3D 变换时,它不像一个有寄宿图的普通图层一样变得像素化。
下面看一下CAShapeLayer常用的属性:
属性 类型 说明
path CGPath?  所绘制图形的路径。
fillColor CGColor? 图形填充颜色。
fillRule CAShapeLayerFillRule

填充规则。

nonZero: 默认值。非零规则。将每个从左到右的路径计数为+1,每个从右到左的路径计数为-1。如果所有交点的和为0,则该点在路径外。如果和是非零的,那么点就在路径内,并且包含它的区域被填充。

evenOdd: 奇偶规则。计算穿越路径的总数。如果交叉点的数量是偶数,则该点在路径外。如果交叉数为奇数,则该点位于路径内,并且应该填充包含该点的区域。

lineCap CAShapeLayerLineCap

指定边线端点的形状。buttroundsquare。其中butt为默认值。

iOS CALayer专用图层理解与使用方法一(CAShapeLayer、CATextLayer、CATransformLayer)_第1张图片

lineDashPattern [NSNumber]?

设置边线的样式,默认为实现,可设置为虚线。

例如如果是[2,3],那么绘制规则为绘制2个单元的实线,然后3个单元不绘制,周而复始。

如果是[10,5,5,5],那么绘制10个单元实线,5个单元不绘制,再绘制5个单元实现,5个单元不绘制,周而复始。

iOS CALayer专用图层理解与使用方法一(CAShapeLayer、CATextLayer、CATransformLayer)_第2张图片

lineDashPhase CGFloat 边线样式的起始位置,即,如果lineDashPattern设置为@[2,2,3,4],lineDashPhase即为第一个长度为2的线的起始位置。
lineJoin CAShapeLayerLineJoin

边线拐点处的样式。miter, round, bevel。默认为miter。

iOS CALayer专用图层理解与使用方法一(CAShapeLayer、CATextLayer、CATransformLayer)_第3张图片

lineWidth CGFloat 设置边线宽度。
strokeColor CGColor? 设置边线颜色。

在了解了CAShapeLayer的常用属性后,就来用一下吧:

    func shaperLayerTest() {
        let width: CGFloat = UIScreen.main.bounds.size.width
        let height: CGFloat = width
        let shapeLayer = CAShapeLayer()
        shapeLayer.frame = CGRect(x: 0, y: 100, width: width, height: height)
        let path = CGMutablePath()
        stride(from: 0, to: CGFloat.pi * 2, by: CGFloat.pi / 6).forEach {
            angle in
            var transform  = CGAffineTransform(rotationAngle: angle)
                .concatenating(CGAffineTransform(translationX: width / 2, y: height / 2))
            
            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 = .evenOdd
        self.view.layer.addSublayer(shapeLayer)
    }

执行效果如下:

iOS CALayer专用图层理解与使用方法一(CAShapeLayer、CATextLayer、CATransformLayer)_第4张图片

除了上面的用法,还有一个需要提一下,那就是圆角

CALayer给圆角提供了一个属性cornerRadius,这个属性支持四个角同时改成圆角。这个属性很方便,但是如果只想针对某一个角进行倒角呢?

采用CAShapeLayer可以指定某一个角进行倒圆角,虽然代码有些多,但还是值得一用的。

参考代码如下:

    func roundCorner() {
        let rect = CGRect(x: 30, y: 100, width: 150, height: 150)
        let raddi = CGSize(width: 20, height: 20)
        let corners: UIRectCorner = [.bottomLeft, .bottomRight]
        let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: raddi)
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = path.cgPath
        shapeLayer.strokeColor = UIColor.red.cgColor
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.lineWidth = 5
        shapeLayer.lineCap = .square
        self.view.layer.addSublayer(shapeLayer)
    }

执行效果如下:

iOS CALayer专用图层理解与使用方法一(CAShapeLayer、CATextLayer、CATransformLayer)_第5张图片

我们可以通过这种方法绘制一个既有圆角又有直角的图形。如果我们想依照此图形来裁剪视图内容,我们可以把CAShapeLayer作为视图的宿主图层,而不是添加一个子视图。

3. CATextLayer

A layer that provides simple text layout and rendering of plain or attributed strings.

一个可以提供简单文本布局以及纯文本或者富文本字符串显示的图层。

class CATextLayer : CALayer

CATextLayer几乎包含了UILabel的所有特性,另外还提供了一些新的特性。并且CATextLayer使用了Core text,渲染要比UILabel快很多。

下面看一些CATextLayer常用的属性:

属性 类型 说明
string Any? 要显示的文本。
font CFTypeRef? 文本字体。可以设置为CTFont或者CGFont,不可以设置为UIFont。
fontSize CGFloat 文本字体大小。
foregroundColor CGColor? 文本字体颜色。
isWrapped Bool 文本是否换行。
alignmentMode CATextLayerAlignmentMode

文本水平对齐方式。

natural:自然对齐方式,默认方式。

left:左对齐。

right:右对齐。

center:居中对齐。

justified:两端对齐。

truncationMode CATextLayerTruncationMode

文本末尾折断方式。

none: 默认方式,如果文本设置了isWrapped为true,那么文本换行显示,直到显示变大折断。

start: 显示不下的话,文档前部分打点显示。

end: 显示不下的话,文档末尾部分打点显示。

middle: 显示不下的话,文档中间部分打点显示。

示例代码如下:

    func textLayerTest() {
        let textLayer = CATextLayer()
        textLayer.backgroundColor = UIColor.white.cgColor
        textLayer.frame = CGRect(x: 50, y: 100, width: 200, height: 200)
        textLayer.string = "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda."
        textLayer.contentsScale = UIScreen.main.scale
        textLayer.foregroundColor = UIColor.black.cgColor
        textLayer.alignmentMode = .natural
        textLayer.isWrapped = true
        textLayer.truncationMode = .end
        let fontName: CFString = "HelveticaNeue-BoldItalic" as CFString
        textLayer.font = CTFontCreateWithName(fontName, 17, nil)
        textLayer.fontSize = 17
        self.view.layer.addSublayer(textLayer)
    }

上面有两个位置需要注意,一个是设置字体,这里采用了CTFontCreateWithName函数设置字体,设置字体大小需要用属性fontSizeCTFontCreateWithName函数里面设置的fontSize不起作用;二是要设置contentsScale属性,对于Retina屏幕,不设置这个属性,显示的文字会有些花掉。设置contentsScale与不设置contentsScale的执行结果分别如下:

iOS CALayer专用图层理解与使用方法一(CAShapeLayer、CATextLayer、CATransformLayer)_第6张图片   iOS CALayer专用图层理解与使用方法一(CAShapeLayer、CATextLayer、CATransformLayer)_第7张图片

这里附带一个显示系统所有字体的方法:

    func showAllSystemFonts() {
        UIFont.familyNames.forEach { (familyName) in
            print(familyName + " : ")
            UIFont.fontNames(forFamilyName: familyName).forEach { (fontName) in
                print("\t\(fontName)")
            }
        }
    }

CATextLayer富文本

既然UILabel有富文本功能,那么CATextLayer也有富文本功能。下面看一下CATextLayer的富文本:

    func textLayerAttributeTest() {
        let textLayer = CATextLayer()
        textLayer.backgroundColor = UIColor.white.cgColor
        textLayer.frame = CGRect(x: 50, y: 100, width: 200, height: 200)
        textLayer.contentsScale = UIScreen.main.scale
        textLayer.foregroundColor = UIColor.black.cgColor
        textLayer.alignmentMode = .natural
        textLayer.isWrapped = true
        textLayer.truncationMode = .end
        
        let text = "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda."
        
        let attributeString = NSMutableAttributedString(string: text)
        
        let attrs1: Dictionary = [NSAttributedString.Key.foregroundColor : UIColor.black.cgColor,
                     NSAttributedString.Key.font : CTFontCreateWithName("HelveticaNeue" as CFString, 15, nil)]
        attributeString.setAttributes(attrs1, range: NSRange(location: 0, length: text.count))
        
        let attrs2: Dictionary = [NSAttributedString.Key.foregroundColor : UIColor.blue.cgColor, NSAttributedString.Key.font : CTFontCreateWithName("HelveticaNeue-BoldItalic" as CFString, 15, nil), NSAttributedString.Key.underlineStyle : NSUnderlineStyle.double.rawValue]
        attributeString.setAttributes(attrs2, range: NSRange(location: 5, length: 4))
        textLayer.string = attributeString
        self.view.layer.addSublayer(textLayer)
    }

执行结果图:

iOS CALayer专用图层理解与使用方法一(CAShapeLayer、CATextLayer、CATransformLayer)_第8张图片

4. CATransformLayer

Objects used to create true 3D layer hierarchies, rather than the flattened hierarchy rendering model used by other CALayer classes.

用来创建真正的3D层次结构的模型,而不是其他图层类使用的平面化层次结构渲染模型。

class CATransformLayer : CALayer

说的通俗一些,CATransformLayer就是构建了一个3D的图层空间,而这个空间装着真正需要显示的图层,与普通图层不同,CATransformLayer不会将其子图层至于z=0的平面上。

下面看一个官方给出的Demo,创建一个CATransformLayer图层,然后再创建三个有不同颜色的普通图层,并加入到CATransformLayer图层中,三个颜色图层大小位置都一样,只是zPosition值不同。

    func transformLayerTest() {
        let layer = CATransformLayer()
             
        func layerOfColor(_ color: UIColor, zPosition: CGFloat) -> CALayer {
            let layer = CALayer()
            layer.frame = CGRect(x: 70, y: 150, width: 100, height: 100)
            layer.backgroundColor = color.cgColor
            layer.zPosition = zPosition
            layer.opacity = 0.5
            
            return layer
        }
             
        layer.addSublayer(layerOfColor(.red, zPosition: 20))
        layer.addSublayer(layerOfColor(.green, zPosition: 30))
        layer.addSublayer(layerOfColor(.blue, zPosition: 40))
             
        var perspective = CATransform3DIdentity
        perspective.m34 = -1.0 / 100
        layer.transform = CATransform3DRotate(perspective, 0.1, 0, 1, 0)
        self.view.layer.addSublayer(layer)
    }

执行结果如下图:

iOS CALayer专用图层理解与使用方法一(CAShapeLayer、CATextLayer、CATransformLayer)_第9张图片

如果我们不用CATransformLayer图层,而是改为CALayer图层,代码如下:

    func transformLayerTest() {
        let layer = CALayer()
             
        func layerOfColor(_ color: UIColor, zPosition: CGFloat) -> CALayer {
            let layer = CALayer()
            layer.frame = CGRect(x: 70, y: 150, width: 100, height: 100)
            layer.backgroundColor = color.cgColor
            layer.zPosition = zPosition
            layer.opacity = 0.5
            
            return layer
        }
             
        layer.addSublayer(layerOfColor(.red, zPosition: 20))
        layer.addSublayer(layerOfColor(.green, zPosition: 30))
        layer.addSublayer(layerOfColor(.blue, zPosition: 40))
             
        var perspective = CATransform3DIdentity
        perspective.m34 = -1.0 / 100
        layer.transform = CATransform3DRotate(perspective, 0.1, 0, 1, 0)
        self.view.layer.addSublayer(layer)
    }

执行结果如下图:

iOS CALayer专用图层理解与使用方法一(CAShapeLayer、CATextLayer、CATransformLayer)_第10张图片

红色和绿色的图层被隐藏掉了,如法看出是一个3D的空间图层。

下面再用CATransformLayer绘制一个立方体,代码如下:

    override func viewDidLoad() {
        super.viewDidLoad()
        
        var transform = CATransform3DIdentity
        transform.m34 = -1.0/500
        self.view.layer.sublayerTransform = transform
        
        var cubeCT = CATransform3DIdentity
        cubeCT = CATransform3DTranslate(cubeCT, 50, 50, 0)
        cubeCT = CATransform3DRotate(cubeCT, -CGFloat(Double.pi/4), 1, 0, 0)
        cubeCT = CATransform3DRotate(cubeCT, -CGFloat(Double.pi/4), 0, 1, 0)
        let cubeLayer = createCube(transform: cubeCT)
        self.view.layer.addSublayer(cubeLayer)
    }
    
    // 创建立方体
    func createCube(transform: CATransform3D) -> CATransformLayer {
        let cubeLayer = CATransformLayer()
        cubeLayer.transform = transform
        
        // 创建第1个面,即面对我们的面,将该面沿着z轴移动50
        var ct = CATransform3DIdentity
        ct = CATransform3DTranslate(ct, 0, 0, 50)
        let face1 = createFaceLayer(transform: ct, color: UIColor.cyan)
        cubeLayer.addSublayer(face1)
        
        // 创建第2个面,即左侧的面,将该面沿着x轴移动-50,然后再沿着y轴旋转-90度
        ct = CATransform3DMakeTranslation(-50, 0, 0)
        ct = CATransform3DRotate(ct, -CGFloat(Double.pi/2), 0, 1, 0)
        let face2 = createFaceLayer(transform: ct, color: UIColor.blue)
        cubeLayer.addSublayer(face2)
        
        // 创建第3个面,即右侧的面,将该面沿着x轴移动50,然后再沿着y轴旋转90度
        ct = CATransform3DMakeTranslation(50, 0, 0)
        ct = CATransform3DRotate(ct, CGFloat(Double.pi/2), 0, 1, 0)
        let face3 = createFaceLayer(transform: ct, color: UIColor.green)
        cubeLayer.addSublayer(face3)
        
        // 创建第4个面,即底部的面,将该面沿着y轴移动50,然后再沿着x轴旋转90度
        ct = CATransform3DMakeTranslation(0, 50, 0)
        ct = CATransform3DRotate(ct, CGFloat(Double.pi/2), 1, 0, 0)
        let face4 = createFaceLayer(transform: ct, color: UIColor.orange)
        cubeLayer.addSublayer(face4)
        
        // 创建第5个面,即顶部的面,将该面沿着y轴移动-50,然后再沿着x轴旋转-90度
        ct = CATransform3DMakeTranslation(0, -50, 0)
        ct = CATransform3DRotate(ct, -CGFloat(Double.pi/2), 1, 0, 0)
        let face5 = createFaceLayer(transform: ct, color: UIColor.brown)
        cubeLayer.addSublayer(face5)
        
        // 创建第6个面,即背对我们的面,将该面沿着z轴移动-50,然后再沿着x轴旋转180度
        ct = CATransform3DMakeTranslation(0, 0, -50)
        ct = CATransform3DRotate(ct, CGFloat(Double.pi), 1, 0, 0)
        let face6 = createFaceLayer(transform: ct, color: UIColor.purple)
        cubeLayer.addSublayer(face6)
        
        return cubeLayer
    }
    
    // 创建立方体的每一个面
    func createFaceLayer(transform: CATransform3D, color: UIColor) -> CALayer {
        let layer = CALayer()
        layer.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
        layer.backgroundColor = color.cgColor
        layer.transform = transform
        return layer
    }

执行结果图如下:

iOS CALayer专用图层理解与使用方法一(CAShapeLayer、CATextLayer、CATransformLayer)_第11张图片

5. 总结

本文主要讲解了CALayer专用图层中的三个图层(CAShapeLayer、CATextLayer、CATransformLayer)的概念属性以及一些用法。

CAShapeLayer一个通过贝塞尔曲线绘制的图层,渲染速度快,高效使用内存,不会被裁边和像素化。

CATextLayer专门用于显示文本的图层,几乎包含了UILabel的所有特性,并且比UILabel渲染快。

CATransformLayer专门用于构建3D空间的图层。

 

以上内容如果不正确的地方,还请路过的朋友指正,谢谢!

本篇文章出自https://blog.csdn.net/guoyongming925的博客,如需转载,请标明出处。

 

你可能感兴趣的:(Core,Animation,iOS,Swift,CAShapeLayer,CATextLayer,Transform)