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

引言

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/770767.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vue实现金钱输入框组件自动带千位逗号

新建PriceInput.vue <template><div id"bord"><el-inputv-model"inputValue"v-bind"$attrs":maxlength"maxlength"input"handleInput"focus"handleFocus"blur"handleBlur"change"h…

自闭症儿童:探索症状背后的多彩内心世界

在星启帆自闭症康复中心&#xff0c;我们每天与一群独特而珍贵的孩子相遇——他们&#xff0c;是自闭症谱系障碍的患儿。自闭症&#xff0c;这一复杂的神经发育障碍&#xff0c;以其多样化的症状表现&#xff0c;为每个孩子的生活轨迹绘上了不同的色彩。 自闭症孩子的症状各异…

Websocket通信实战项目(js)(图片互传应用)(下)客户端H5+css+js实现

Rqtz : 个人主页 ​ 共享IT之美&#xff0c;共创机器未来 Sharing the Beauty of IT and Creating the Future of Machines Together 目录 起始 客户端GUI Javascripts连接websocket 使用localStorage保存用户输入的IP Websocket连接成功 Websocket接收数据 解析…

【Linux】正确的关机方法

1. Linux正确的关机方式 如何关机呢&#xff1f;我想&#xff0c;很多朋友在DOS年代已经有在玩计算机了。在当时我们关闭DOS的系统时&#xff0c;常常是直接关闭电源开关&#xff0c;而Windows 在你不爽的时候&#xff0c;按着电源开关四秒也可以关机&#xff0c;但是在Linux则…

旧衣回收小程序:减少资源浪费,提高回收效率

当下&#xff0c;旧衣服回收成为了大众热衷的事&#xff0c;不少居民都会把闲置的衣物进行回收&#xff0c;旧衣回收行业逐渐火爆。不过&#xff0c;传统的旧衣回收模式已经不符合当下时代发展&#xff0c;具有较大的不便利性。 因此&#xff0c;为解决这一问题&#xff0c;线…

PG实践|内置函数之GENERATE_SERIES之深入理解(二)

&#x1f4eb; 作者简介&#xff1a;「六月暴雪飞梨花」&#xff0c;专注于研究Java&#xff0c;就职于科技型公司后端工程师 &#x1f3c6; 近期荣誉&#xff1a;华为云云享专家、阿里云专家博主、腾讯云优秀创作者、ACDU成员 &#x1f525; 三连支持&#xff1a;欢迎 ❤️关注…

使用Vue CLI方式创建Vue3.0应用程序

Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统。新版本的 Vue CLI 的包名由原来的 vue-cli 改成了 vue/cli。 在开发大型项目时&#xff0c;需要考虑项目的组织结构、项目构建和部署等问题。如果手动完成这些配置工作&#xff0c;工作效率会非常低。为此&#xff0c;Vue.…

Rocky Linux 9 快速安装docker 教程

前述 CentOS 7系统将于2024年06月30日停止维护服务。CentOS官方不再提供CentOS 及后续版本&#xff0c;不再支持新的软件和补丁更新。CentOS用户现有业务随时面临宕机和安全风险&#xff0c;并无法确保及时恢复。由于 CentOS Stream 相对不稳定&#xff0c;刚好在寻找平替系统…

Python学生信息管理系统(完整代码)

引言&#xff1a;&#xff08;假装不是一个大学生课设&#xff09;在现代教育管理中&#xff0c;学生管理系统显得尤为重要。这种系统能够帮助教育机构有效地管理学生资料、成绩、出勤以及其他教育相关活动&#xff0c;从而提高管理效率并减少人为错误。通过使用Python&#xf…

IDEA版本推荐

推荐版本&#xff1a; IDEA 2024.1.4 下载链接&#xff1a;IDEA下载 &#xff08;下载时可以往下拖&#xff0c;选到自己想要的版本哦&#xff09; 本人由于项目开发需要&#xff0c;陆续用过几个版本的IDEA&#xff0c;包括&#xff1a; IDEA 2020.2.4 。这是在看韩顺平老师…

昇思25天学习打卡营第9天|CycleGAN图像风格迁移互换

文章目录 昇思MindSpore应用实践基于MindSpore的CycleGAN图像风格迁移互换1、CycleGAN 概述2、生成器部分3、判别器部分4、优化器和损失函数5、模型训练6、模型推理 Reference 昇思MindSpore应用实践 本系列文章主要用于记录昇思25天学习打卡营的学习心得。 基于MindSpore的C…

打造商贸物流“产-供-销”、“仓-运-配”全流程供应链

在当今全球化的商业环境中&#xff0c;商贸物流平台的搭建成为企业提升效率、降低成本并增强市场竞争力的关键因素。在现代商业环境中&#xff0c;商贸与物流之间的紧密协作是业务成功的关键因素。然而&#xff0c;许多组织面临着信息不对称、资源配套不足、以及系统间隔离等痛…

Windows的管理工具

任务计划程序&#xff1a;这是一个用来安排任务自动运行的工具。你可以在这里创建新的任务&#xff0c;设定触发条件&#xff0c;并指定任务的操作。 事件查看器&#xff1a;这是一套日志记录和分析工具&#xff0c;&#xff0c;你可以了解到系统的工作状况&#xff0c;帮助诊…

Spark大数据处理:技术、应用与性能优化(全)PDF书籍推荐分享

本书从一个系统化的视角&#xff0c;秉承大道至简的主导思想&#xff0c;介绍Spark中最值得关注的内 容&#xff0c;讲解Spark部署、开发实战&#xff0c;并结合Spark的运行机制及拓展&#xff0c;帮读者开启Spark技术之旅。 Spark大数据处理&#xff1a;技术、应用与性能优化…

阿里云邮件推送邮件发送失败的问题排查解决

阿里云邮件推送为何失败&#xff1f;解决邮件推送失败的步骤指南&#xff01; 即便是功能强大的阿里云邮件推送服务&#xff0c;也可能在实际使用中遇到邮件发送失败的问题。AokSend将详细介绍如何排查和解决阿里云邮件推送邮件发送失败的问题。 阿里云邮件推送&#xff1a;验…

深入浅出 LangChain 与智能 Agent:构建下一代 AI 助手

我们小时候都玩过乐高积木。通过堆砌各种颜色和形状的积木&#xff0c;我们可以构建出城堡、飞机、甚至整个城市。现在&#xff0c;想象一下如果有一个数字世界的乐高&#xff0c;我们可以用这样的“积木”来构建智能程序&#xff0c;这些程序能够阅读、理解和撰写文本&#xf…

6.26.3 基于Transformer的深度神经网络在数字乳腺断层合成图像上的乳腺癌分类

开发一种有效的深度神经网络模型&#xff0c;该模型结合了相邻图像部分的上下文&#xff0c;以检测数字乳腺断层合成(DBT)图像上的乳腺癌。 数字乳房断层合成(DBT)是一种医学成像技术&#xff0c;其中检测器围绕患者以有限角度旋转并记录多幅图像。然后将这些图像重建为二维(2D…

盛元广通打造智慧校园实验室安全管理系统

盛元广通智慧校园实验室安全管理系统以安全为重点&#xff0c;构建由学校、二级单位、实验室组成的三级联动的实验室安全多级管理体系、多类用户角色&#xff0c;内置教育部标准检查表&#xff0c;支撑实验室相关业务过程的智慧管理。实现通过PC端/手机移动端开展检查工作、手机…

一个opencv实现检测程序

引言 图像处理是计算机视觉中的一个重要领域&#xff0c;它在许多应用中扮演着关键角色&#xff0c;如自动驾驶、医疗图像分析和人脸识别等。边缘检测是图像处理中的基本任务之一&#xff0c;它用于识别图像中的显著边界。本文将通过一个基于 Python 和 OpenCV 的示例程序&…

Vue86-Vuex中的getters属性

一、getters的使用 1-1、index.js中getters的书写 计算属性computed靠return获得返回值&#xff01; 1-2、组件中getters的调用 state是数据源&#xff0c;getters是拿着数据源里的东西进行一番加工。像极了&#xff1a;data和computed 二、小结