六.核心动画 - 特殊图层①

引言

本专栏到目前为止已经介绍了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提供很多特殊图层,它们适用于不同的特殊环境,我们在本篇博客中先来介绍这四种图层,下一篇博客我们将会讨论更多的专用图层。

  • 29
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值