九.核心动画 - 显式动画

引言

本篇博客紧接着上一篇的隐式动画开始介绍显式动画。隐式动画是创建动态页面的一种简单的直接的方式,也是UIKit的动画机制基础。但是它并不能涵盖所有的动画类型。

显式动画

接下来我们就来研究另外一种动画显式动画,它能够对一些属性做指定的动画,或者非线性动画。

显式动画 - 属性动画

给一个图层的属性添加动画时,我们需要借助CAAnimationDelegate代理,这个代理我们可以在CAAnimation的文件中找到,它有两个方法:

动画已经开始

    optional func animationDidStart(_ anim: CAAnimation)

动画已经结束

optional func animationDidStop(_ anim: CAAnimation, finished flag: Bool)

下面的例子中我们使用属性动画来修改图层的颜色,并在动画结束之后来设置图层最终颜色。

这里面有一个需要注意的地方,如果我们给单独的图层添加动画,需要在一个新的事物中来进行,并且禁用图层的默认行为,否则动画会发生两次,一次是我们设置的显式动画,一个是图层默认的隐式动画。

代码如下:

class ViewController: UIViewController, CAAnimationDelegate{let colorLayer = CALayer()override func viewDidLoad() {super.viewDidLoad()// Do any additional setup after loading the view.colorLayer.frame = CGRect(x: 50, y: 100, width: 100, height: 100)colorLayer.backgroundColor = UIColor.red.cgColorself.view.layer.addSublayer(colorLayer)}override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {let colors = [UIColor.red.cgColor, UIColor.green.cgColor, UIColor.blue.cgColor]let animation = CABasicAnimation()animation.keyPath = "backgroundColor"animation.toValue = colors.randomElement()animation.duration = 1.0animation.delegate = selfcolorLayer.add(animation, forKey: nil)}func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {if let animation = anim as? CABasicAnimation {CATransaction.begin()CATransaction.setDisableActions(true)let cgColor = animation.toValuecolorLayer.backgroundColor =  (cgColor as! CGColor)CATransaction.commit()}}}

动画的结束事件,是使用代理的方法来实现的,这样会有一个问题。就是假如一个控制器中有多个图层进行动画,那么所有的动画都会使用这一个回调方法,这样的话我们就需要判断是哪个图层动画的回调。

在添加动画时我们发现open func add(_ anim: CAAnimation, forKey key: String?)有一个key属性,目前设置的为nil。事实上它就是东湖的唯一标识符,我们给它设置一个非空的唯一字符串,在回调的时候就可以对所有图层进行循环,调用-animationForKey:来比对结果。

我们来修改一下代码:

        colorLayer.add(animation, forKey: "colorChange")
    func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {if colorLayer.animation(forKey: "colorChange") == anim {if let animation = anim as? CABasicAnimation {CATransaction.begin()CATransaction.setDisableActions(true)let cgColor = animation.toValuecolorLayer.backgroundColor =  (cgColor as! CGColor)CATransaction.commit()}}}

但是当有很多很多图层有很多很多动画的话,这个方法就显得不太优雅了。

不过我们还有更简单的版本。CAAnimation也显示了KVC协议,所以我们可以使用open func setValue(_ value: Any?, forKey key: String)和open func value(forKey key: String) -> Any?两个方法来存取属性。但是CAAnimation还有一个不同的地方,它更新时一个NSDictionary,我们可以随意设置键值对。

这就意味着我们可以对动画打任何类型的标签,下面我们就来写个例子感受一下这个特性:

class ViewController: UIViewController, CAAnimationDelegate{let firstView = UIView()let secondView = UIView()let thirdView = UIView()override func viewDidLoad() {super.viewDidLoad()self.view.addSubview(firstView)firstView.backgroundColor = .redfirstView.frame = CGRect(x: 40.0, y: self.view.bounds.size.height - 300.0, width: 40.0, height: 10.0)self.view.addSubview(secondView)secondView.backgroundColor = .greensecondView.frame = CGRect(x: 80.0, y: self.view.bounds.size.height - 300.0, width: 40.0, height: 10.0)self.view.addSubview(thirdView)thirdView.backgroundColor = .bluethirdView.frame = CGRect(x: 120.0, y: self.view.bounds.size.height - 300.0, width: 40.0, height: 10.0)}override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {addAnimation(view: firstView, height: 100.0)addAnimation(view: secondView, height: 200.0)addAnimation(view: thirdView, height: 300.0)}func addAnimation(view:UIView,height:CGFloat) {let animation = CABasicAnimation()animation.keyPath = "bounds.size.height"animation.toValue = heightanimation.duration = 1.0animation.delegate = selfanimation.setValue(view, forKey: "view")view.layer.add(animation, forKey: nil)}func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {guard let anim = anim as? CABasicAnimation else {return}if let view = anim.value(forKey: "view") as? UIView {let height = anim.toValue as? CGFloatview.layer.bounds.size.height = height!}}}

点击屏幕后发现,每个动画的结果都对应到了自己的图层,效果如下:

可以做动画的属性和虚拟属性非常的多,我们就来列举一些常见的几何属性吧

  • tranform.rotation.x        按x轴旋转的弧度
  • tranform.rotation.y        按y轴旋转的弧度
  • tranform.rotation.z        按z轴旋转的弧度
  • tranform.rotation        按z轴旋转的弧度,和tranform.rotation.z效果一样
  • tranform.scale.x        在x轴按比例放大缩小
  • tranform.scale.y        在y轴按比例放大缩小
  • tranform.scale.z        在z轴按比例放大缩小
  • tranform.scale        整体按比例放大缩小
  • transform.translation.x        沿x轴平移
  • transform.translation.y        沿y轴平移
  • transform.translation.z        沿z轴平移
  • transform.translation        x,y坐标均发生改变
  • transform        CATransform3D 4xbounds4矩阵
  • bounds        layer大小
  • position        layer位置
  • anchorPoint        锚点位置
  • cornerRadius        圆角大小
  • ZPosition        Z轴位置

显式动画 - 关键帧动画

CAKeyframeAnimation是另一种UIKit没有暴露出来的且功能十分强大的类。和CABasicAnimation一样它也是继承自CAPropertyAnimation,并且它也作用于单一的一个属性,不同的是它不是只设置一个起始值和一个结束值。而是可以设置一连串随意的值来做动画。

关键帧动画起源于传动动画。它的主要思想是指主导的动画在显著改变发生时重绘当前帧,也就是关键帧。而关键帧与关键帧之间的绘制则由Core Animation通过推算来完成。

我们来使用一个例子,通过关键帧动画来修改图层的颜色,代码如下:

class ViewController: UIViewController, CAAnimationDelegate{let colorLayer = CALayer()override func viewDidLoad() {super.viewDidLoad()colorLayer.frame = CGRect(x: 50, y: 100, width: 100, height: 100)colorLayer.backgroundColor = UIColor.red.cgColorself.view.layer.addSublayer(colorLayer)}override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {let animation = CAKeyframeAnimation()animation.keyPath = "backgroundColor"animation.duration = 2.0animation.values = [UIColor.red.cgColor, UIColor.green.cgColor, UIColor.blue.cgColor,UIColor.yellow.cgColor]animation.keyTimes = [0.0, 0.25, 0.5, 0.75, 1.0]colorLayer.add(animation, forKey: nil)}

效果如下:

CAKeyframeAnimation还有另外一种方式来指定动画,就是使用CGPath。path属性可以用一种更直观的方式来描述动画,我们这次采用UIKit提供的UIBezierPath来创建path,并且为了更直观我们使用CAShapeLayer将path渲染出来。然后我们在设置CAKeyframeAnimation来创建我们的动画。

代码如下:

        let bezierPath = UIBezierPath()bezierPath.move(to: CGPoint(x: 50, y: 150))bezierPath.addCurve(to: CGPoint(x: 300, y: 150), controlPoint1: CGPoint(x: 150, y: 50), controlPoint2: CGPoint(x: 200, y: 250))let shapeLayer = CAShapeLayer()shapeLayer.path = bezierPath.cgPathshapeLayer.fillColor = UIColor.clear.cgColorshapeLayer.strokeColor = UIColor.red.cgColorshapeLayer.lineWidth = 3.0self.view.layer.addSublayer(shapeLayer)let layer = CALayer()layer.frame = CGRect(x: 50 - 40, y: 150 - 40, width: 40, height: 40)layer.contents = UIImage(named: "a-meiguihuahuameigui")?.cgImageself.view.layer.addSublayer(layer)let animation = CAKeyframeAnimation()animation.keyPath = "position"animation.path = bezierPath.cgPathanimation.duration = 4.0layer.add(animation, forKey: nil)

效果如下:

还可以体验一下animation.rotationMode = .rotateAuto这个属性。

        let animation = CAKeyframeAnimation()animation.keyPath = "position"animation.path = bezierPath.cgPathanimation.duration = 4.0animation.rotationMode = .rotateAutolayer.add(animation, forKey: nil)

你会发现,花会沿着曲线运动时会发生旋转,使自己总垂直于曲线。

结语

本篇博客介绍了两个最常用的显式动画,属性动画和关键帧动画。大家在实际开发过程中应该经常会使用到。接下来的博客我们会继续讨论显式动画,进行动画的组合,过渡动画,以及取消动画。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

揭秘”大模型加速器”如何助力大模型应用

文章目录 一、大模型发展面临的问题二、“大模型加速器”助力突破困难2.1 现场效果展示2.1.1 大模型加速器——文档解析引擎2.2.2 图表数据提取 三、TextIn智能文档处理平台3.1 在线免费体验3.1.1 数学公式提取3.1.2 表格数据提取 四、acge文本向量化模型4.1 介绍4.2 技术创新4…

从0开始的STM32HAL库学习2

外部中断(HAL库GPIO讲解) 今天我们会详细地学习STM32CubeMX配置外部中断&#xff0c;并且讲解HAL库的GPIO的各种函数。 准备工作&#xff1a; 1、STM32开发板&#xff08;我的是STM32F103C8T6&#xff09; 2、STM32CubeMx软件、 IDE&#xff1a; Keil软件 3、STM32F1xx/ST…

前端使用Vue和Element实现可拖动弹框效果,且不影响底层元素操作,Cesium作为底图(可拖拽的视频实时播放弹框,底层元素可以正常操作)

简述&#xff1a;在前端开发中&#xff0c;弹框和实时视频播放是常见的需求。这里来简单记录一下&#xff0c;如何使用Vue.js和Element UI实现一个可拖动的弹框&#xff0c;并在其中播放实时视频。同时&#xff0c;确保在拖拽弹框时&#xff0c;底层元素仍然可以操作。这里来记…

Effective C++笔记之二十一:One Definition Rule(ODR)

ODR细节有点复杂&#xff0c;跨越各种情况。基本内容如下&#xff1a; ●普通&#xff08;非模板&#xff09;的noninline函数和成员函数、noninline全局变量、静态数据成员在整个程序中都应当只定义一次。 ●class类型&#xff08;包括structs和unions&#xff09;、模板&…

钡铼4G无线RTU助力智慧能源发展实现电网远程调控

随着全球对清洁能源和高效能源管理的需求日益增长&#xff0c;智慧能源技术正逐渐成为推动可持续发展的重要驱动力。在这一背景下&#xff0c;钡铼4G无线远程终端单元正在为智慧能源的发展和电网的远程调控提供强有力的支持。 钡铼4G无线RTU&#xff1a;智慧能源的神经网络 钡…

顺序结构 ( 五 ) —— 数据输入输出 【互三互三】

文章目录 &#x1f341;序 &#x1f341;一、字符输入函数getchar &#x1f341;二、字符输出函数putchar &#x1f341;三、通过cout流输出数据 &#x1f341;四、通过cin流读入数据 &#x1f341;五、格式化输入函数scanf &#x1f341;六、格式化输出函数printf &…

【python】QWidget父子关系,控件显示优先级原理剖析与应用实战演练

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

【C语言】C语言编译链接和Win32API简单介绍

目录 翻译环境和运行环境翻译环境编译器预处理&#xff08;预编译&#xff09;编译链接 执行环境 Win32API是什么控制台程序控制台获取坐标COORDGetStdHandle函数GetConsoleCursorinfo函数CONSOLE_CURSOR_INFOSetConsoleCursorInfo函数SetConsoleCursorPostion函数GetAsyncKeyS…

数字化时代的供应链管理综合解决方案

目录 引言背景与意义供应链管理综合解决方案的目标 &#x1f4c4;供应链管理系统主要功能系统优势 &#x1f4c4;物流管理系统主要功能系统优势 &#x1f4c4;订单管理系统主要功能应用场景 &#x1f4c4;仓储管理系统系统亮点主要功能系统优势 &#x1f4c4;商城管理系统主要功…

【MyBatis】——入门基础知识必会内容

&#x1f3bc;个人主页&#xff1a;【Y小夜】 &#x1f60e;作者简介&#xff1a;一位双非学校的大二学生&#xff0c;编程爱好者&#xff0c; 专注于基础和实战分享&#xff0c;欢迎私信咨询&#xff01; &#x1f386;入门专栏&#xff1a;&#x1f387;【MySQL&#xff0…

MySql性能调优01-[数据结构和索引]

数据结构和索引 什么是索引索引的种类常见索引数据结构和区别二叉树 红黑树 什么是索引 索引的种类 在Mysql中索引是在存储引擎层实现的&#xff0c;而不是在服务层实现的 按数据结构分&#xff1a;Btree索引、Hash索引、Full-text索引按存储结构分&#xff1a;聚簇索引、非聚…

数据结构——约瑟夫环C语言链表实现

约瑟夫环问题由古罗马史学家约瑟夫&#xff08;Josephus&#xff09;提出&#xff0c;他参加并记录了公元66—70年犹太人反抗罗马的起义。在城市沦陷之后&#xff0c;他和40名死硬的将士在附近的一个洞穴中避难。起义者表示“宁为玉碎不为瓦全”&#xff0c;约瑟夫则想“留得青…

go语言Gin框架的学习路线(六)

gin的路由器 Gin 是一个用 Go (Golang) 编写的 Web 框架&#xff0c;以其高性能和快速路由能力而闻名。在 Gin 中&#xff0c;路由器是框架的核心组件之一&#xff0c;负责处理 HTTP 请求并将其映射到相应的处理函数上。 以下是 Gin 路由器的一些关键特性和工作原理的简要解释…

第十八章 Express multer 文件上传

本章将学习Express multer 文件上传 &#xff0c;因为Nest 的文件上传是基于 Express 的中间件 multer 实现的&#xff0c;所以在学习 Nest 文件上传之前&#xff0c;我们先学习下 multer 包 首先先创建 multer-test 文件夹执行下面代码 创建package.json npm init -y接着安装…

单例模式的简单理解

单例模式 前言一、单例模式是什么二、单例模式的使用饿汉模式单线程下的懒汉模式多线程下的懒汉模式&#xff08;优化懒汉模式&#xff09;加锁 三、总结 前言 设计模式是将一些经典的问题场景进行整合归纳&#xff0c;并提供一些解决方案&#xff0c;相当于一种“套路”。 熟…

数据仓库介绍_维度表(三)

维度表概述 维度表是维度建模的基础和灵魂。前文提到&#xff0c;事实表紧紧围绕业务过程进行设计&#xff0c;而维度表则围绕业务过程所处的环境进行设计。维度表主要包含一个主键和各种维度字段&#xff0c;维度字段称为维度属性。 表设计步骤 确定维度&#xff08;表&…

SQL 针对上面的salaries表emp_no字段创建索引idx_emp_no

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 描述 针对salaries…

【开源合规】开源许可证风险场景详细解读

文章目录 前言关于BlackDuck许可证风险对比图弱互惠型许可证举个例子具体示例LGPL系列LGPL-2.0-onlyLGPL-2.0-or-laterLGPL-2.1-onlyLGPL-2.1-or-laterLGPL-3.0-onlyLGPL-3.0-or-laterMPL系列MPL-1.0MPL-1.1MPL-2.0EPL系列EPL-1.0EPL-2.0互惠型许可证GPL系列GPL-1.0GPL-2.0GPL-…

3.相机标定原理及代码实现(opencv)

1.相机标定原理 相机参数的确定过程就叫做相机标定。 1.1 四大坐标系及关系 &#xff08;1&#xff09;像素坐标系&#xff08;单位&#xff1a;像素&#xff08;pixel&#xff09;&#xff09; 像素坐标系是指相机拍到的图片的坐标系&#xff0c;以图片的左上角为坐标原点&a…

合合信息大模型加速器亮相WAIC大会:文档解析与文本识别新突破

合合信息大模型加速器亮相WAIC大会&#xff1a;文档解析与文本识别新突破 文章目录 合合信息大模型加速器亮相WAIC大会&#xff1a;文档解析与文本识别新突破前言合合信息TextIn平台&#xff1a;智能文档处理的领军者文档解析引擎&#xff1a;百页文档秒级处理大模型的发展背景…