引言
本专栏到目前为止已经介绍了CALayer,了解了它的绘画和动画功能。但是Core Animation图层不仅仅能够用于图片和颜色,本篇博客就来介绍一下一些CALayer的子类特殊图层,来进一步扩展Core Animation的绘图能力。
特殊图层
Core Animation提供了大量的特殊图层用来实现特定的效果,下面我们来一一介绍一下。
CAShapeLayer
CAShapeLayer其实在我们的实际开发过程中还是经常会u用到的,它是通过一个矢量图形来绘制的CALayer的子类。可以指定它的颜色和线宽等属性,使用CGPath来定义要绘制的图形,将CGPath赋值给CAShapeLayer就会自动渲染出来了。
当然了我们也可以使用Core Graphics直接向原始的CALayer中绘制一个路径,不如CAShapeLayer相对而言有很多优点:
- 渲染速度快。它使用了硬件加速,绘制会比Core Graphics快很多。
- 高效使用内存。CAShaperLayer不需要向CALayer一样创建一个寄宿图像,所以无论图像多大,都不会占用太多的内存。
- 不会出现像素化。当给一个CAShaperLayer做变换时,它不会像一个有寄宿图的普通图层变得像素化。
使用CAShapeLayer绘制CGPath形状
CAShapeLayer可以绘制一切能够通过CGPath来表示的形状,我们可以控制一些属性比如lineWidth线宽,lineCap线条结尾的样式,lineJoin线条直接结合的样式,但是这些属性只有一次设置的机会。如果想用不同的颜色或者风格来绘制多个形状,就需要创建多个图层。
我们来创建一个简单的火柴人,CAShaperLayer的path属性是CGPathRef类型,我们使用UIBezierPath来创建图层的路径。
代码如下:
let path = UIBezierPath()
// 头
path.move(to: CGPoint(x: 175, y: 100))
path.addArc(withCenter: CGPoint(x: 150, y: 100), radius: 25, startAngle: 0, endAngle: Double.pi*2 , clockwise: true)
path.move(to: CGPoint(x: 150, y: 125))
path.addLine(to: CGPoint(x: 150, y: 175))
path.addLine(to: CGPoint(x: 125, y: 225))
path.move(to: CGPoint(x: 150, y: 175))
path.addLine(to: CGPoint(x: 175, y: 225))
path.move(to: CGPoint(x: 100, y: 150))
path.addLine(to: CGPoint(x: 200, y: 150))
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.lineWidth = 5
shapeLayer.lineCap = .round
shapeLayer.lineJoin = .round
view.layer.addSublayer(shapeLayer)
效果如下:
使用CAShapeLayer设置圆角
通常我们使用CALayer的cornerRadius来设置圆角,但它会将所有的角都设置为圆角,有的时候并不能满足我们的需求,比如我们只需要给某一个或者某两个角设置圆角时,这个时候使用CAShapeLayer设置圆角的优势就体现出来了,它不光可以设置圆角,还可以单独设置指定圆角。
使用CAShapeLayer创建圆角(需要借助图层蒙版奥):
let redView = UIView()
redView.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
redView.backgroundColor = UIColor.red
self.view.addSubview(redView)
let rect = CGRect(x: 0, y: 0, width: 100, height: 100)
let size = CGSize(width: 20, height: 20)
// 设置圆角 左上和右上
let corners = UIRectCorner(rawValue: UIRectCorner.RawValue(UInt8(UIRectCorner.topLeft.rawValue) | UInt8(UIRectCorner.topRight.rawValue)))
let path = UIBezierPath.init(roundedRect: rect, byRoundingCorners: corners, cornerRadii: size)
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
redView.layer.mask = shapeLayer
效果如下:
CATextLayer
Core Animation提供了一个CALayer的子类CATextLayer,它以图层的形式包含了UILabel几乎所有的功能,并且还提供一些额外的特性。
CATextLayer的渲染速度也要比UILabel快得多,CATextLayer使用了Core text进行绘制。
下面我们来使用一下它:
let textLayer = CATextLayer()
textLayer.frame = CGRect(x: 100, y: 100, width: 200, height: 100)
textLayer.string = "Core Animation提供了一个CALayer的子类CATextLayer,它以图层的形式包含了UILabel几乎所有的功能,并且还提供一些额外的特性。"
textLayer.foregroundColor = UIColor.red.cgColor
textLayer.alignmentMode = .left
textLayer.isWrapped = true
textLayer.contentsScale = UIScreen.main.scale
let font = UIFont.systemFont(ofSize: 14, weight: .medium)
textLayer.fontSize = font.pointSize
let fontRef = CGFont(font.fontName as CFString)
textLayer.font = fontRef
self.view.layer.addSublayer(textLayer)
效果如下:
这里面有一个我们非常熟悉的属性,在我们设置寄宿图的时候曾经用到过,contentsScale,当我们直接使用图层的时候经常需要主要到这个属性。
CATextLayer的font属性也不是一个UIFont类型,而是 CFTypeRef 类型这样我们可以根据具体的字来决定是使用CGFontRef还是CTFontRef。同时字体大小是使用fontSize属性单独设置的。
CATextLayer的string属性也不是String而是一个Any类型,在OC中就是id类型,这样我们既可以使用String也可以使用NSAttributedString来指定文本的内容。
CATextLayer显示富文本代码如下:
let textLayer = CATextLayer()
textLayer.frame = CGRect(x: 100, y: 100, width: 200, height: 150)
textLayer.alignmentMode = .left
textLayer.isWrapped = true
textLayer.contentsScale = UIScreen.main.scale
let font = UIFont.systemFont(ofSize: 14, weight: .medium)
textLayer.fontSize = font.pointSize
let fontRef = CGFont(font.fontName as CFString)
textLayer.font = fontRef
self.view.layer.addSublayer(textLayer)
let string = "Core Animation提供了一个CALayer的子类CATextLayer,它以图层的形式包含了UILabel几乎所有的功能,并且还提供一些额外的特性。"
let attributedString = NSMutableAttributedString(string: string,attributes: [
NSAttributedString.Key.font: font,
NSAttributedString.Key.foregroundColor: UIColor.orange,
NSAttributedString.Key.kern: 2,
NSAttributedString.Key.paragraphStyle: {
let style = NSMutableParagraphStyle()
style.alignment = .left
style.lineSpacing = 10
return style
}(),
NSAttributedString.Key.shadow: {
let shadow = NSShadow()
shadow.shadowOffset = CGSize(width: 2, height: 2)
shadow.shadowColor = UIColor.black
shadow.shadowBlurRadius = 2
return shadow
}()
])
textLayer.string = attributedString
效果如下:
利用CATextLayer的特性,我们事实上完全可以可以自己来实现一个UILabel的替代品。
CATransformLayer
在上一篇介绍的图层的3D变换中我们曾提到,每个图层都存在于一个不同的3D空间,所有的图层实际上是被它的父图层扁平化了,这样我们就不太可能让一个3D图层独立的进行变换。
不过CATransformLayer可以解决这个问题,CATransformLayer不同于普通的CALayer,它不能显示自己的内容,只有当存在了一个能作用于子图层的变换它才真正的存在。CATransformLayer并不会扁平化它的子图层,所以可以用于构造一个层次的3D结构。
我们来使用CATransformLayer重新创建一下上一篇博客中的立方体吧,我们来创建两个不同的立方体,并让每个立方体做不同的变换来看一下效果。
代码如下:
var perspective = CATransform3DIdentity
perspective.m34 = -1.0/500.0
self.view.layer.sublayerTransform = perspective
// 创建立方体1
var cube1Transform = CATransform3DIdentity
cube1Transform = CATransform3DTranslate(cube1Transform, -100, 0, 0)
cube1Transform = CATransform3DRotate(cube1Transform, -CGFloat.pi/10, 0, 1, 0)
let cube1 = buildCube(cubeTransform: cube1Transform)
self.view.layer.addSublayer(cube1)
// 创建立方体2
var cube2Transform = CATransform3DIdentity
cube2Transform = CATransform3DTranslate(cube2Transform, 100, 0, 0)
cube2Transform = CATransform3DRotate(cube2Transform, CGFloat.pi/4, 1, 0, 0)
cube2Transform = CATransform3DRotate(cube2Transform, CGFloat.pi/4, 0, 1, 0)
let cube2 = buildCube(cubeTransform: cube2Transform)
self.view.layer.addSublayer(cube2)
func buildCube(cubeTransform:CATransform3D) -> CALayer {
let cube = CATransformLayer()
// face 1
var transform = CATransform3DMakeTranslation(0, 0, 50)
cube.addSublayer(buildFace(contentLayer: cube, color: UIColor.red, transform: transform, index: 1))
// face 2
transform = CATransform3DMakeTranslation(50, 0, 0)
transform = CATransform3DRotate(transform, CGFloat.pi/2, 0, 1, 0)
cube.addSublayer(buildFace(contentLayer: cube, color: UIColor.green, transform: transform, index: 2))
// face 3
transform = CATransform3DMakeTranslation(0, -50, 0)
transform = CATransform3DRotate(transform, CGFloat.pi/2, 1, 0, 0)
cube.addSublayer(buildFace(contentLayer: cube, color: UIColor.blue, transform: transform, index: 3))
// face 4
transform = CATransform3DMakeTranslation(0, 50, 0)
transform = CATransform3DRotate(transform, -CGFloat.pi/2, 1, 0, 0)
cube.addSublayer(buildFace(contentLayer: cube, color: UIColor.yellow, transform: transform, index: 4))
// face 5
transform = CATransform3DMakeTranslation(-50, 0, 0)
transform = CATransform3DRotate(transform, -CGFloat.pi/2, 0, 1, 0)
cube.addSublayer(buildFace(contentLayer: cube, color: UIColor.orange, transform: transform, index: 5))
// face 6
transform = CATransform3DMakeTranslation(0, 0, -50)
transform = CATransform3DRotate(transform, CGFloat.pi, 0, 1, 0)
cube.addSublayer(buildFace(contentLayer: cube, color: UIColor.purple, transform: transform, index: 6))
cube.position = CGPoint(x: self.view.bounds.size.width*0.5, y: self.view.bounds.size.height*0.5)
cube.transform = cubeTransform
return cube
}
func buildFace(contentLayer:CALayer,color:UIColor,transform:CATransform3D,index:Int) -> CALayer {
let face = CALayer()
face.frame = CGRect(x: -50, y: -50, width: 100, height: 100)
face.backgroundColor = color.cgColor
face.transform = transform
return face
}
效果如下:
CAGradientLayer
渐变图层,这个在开发过程中使用的频率非常非常高,它专门用来生成两种或者更多颜色的平滑渐变,而且在绘制时也使用了硬件加速。
CAGradientLayer有很多属性共同定义了渐变的样式:
- colors:颜色数组,元素CGColor类型,定义了渐变色颜色。
- locations:颜色的位置数组,定义了每个颜色的在渐变中的位置和过渡点。
- startPoint:颜色开始位置,是个CGPoint类型{0,0}表示左上角,比如从左侧开始那么应该是{0,0.5}
- endPoint:颜色结束位置,是个CGPoint类型{0,0}表示左上角,比如从左侧开始到右侧结束那么应该是{1,0.5}
下面来使用它创建两个渐变:
let gradientLayer = CAGradientLayer()
gradientLayer.frame = CGRect(x: 100, y: 100, width: 200, height: 200)
gradientLayer.colors = [UIColor.red.cgColor,UIColor.blue.cgColor]
gradientLayer.startPoint = CGPoint(x: 0, y: 0)
gradientLayer.endPoint = CGPoint(x: 1, y: 1)
gradientLayer.locations = [0.0,1.0]
self.view.layer.addSublayer(gradientLayer)
let gradientLayer1 = CAGradientLayer()
gradientLayer1.frame = CGRect(x: 100, y: 400, width: 200, height: 200)
gradientLayer1.colors = [UIColor.red.cgColor,UIColor.blue.cgColor,UIColor.green.cgColor]
gradientLayer1.startPoint = CGPoint(x: 0.5, y: 0)
gradientLayer1.endPoint = CGPoint(x: 0.5, y: 1)
gradientLayer1.locations = [0.0,0.5,1.0]
self.view.layer.addSublayer(gradientLayer1)
效果如下:
总结
Core Animation提供很多特殊图层,它们适用于不同的特殊环境,我们在本篇博客中先来介绍这四种图层,下一篇博客我们将会讨论更多的专用图层。