使用CADisplayLink实现UILabel动画特效

在开发时,我们有时候会遇到需要定时对UIView进行重绘的需求,进而让view产生不同的动画效果。

本文项目

效果图

初探 CADisplayLink

定时对View进行定时重绘可能会第一时间想到使用NSTimer,但是这样的动画实现起来是不流畅的,因为在timer所处的runloop中要处理多种不同的输入,导致timer的最小周期是在50到100毫秒之间,一秒钟之内最多只能跑20次左右。

但如果我们希望在屏幕上看到流畅的动画,我们就要维持60帧的刷新频率,也就意味着每一帧的间隔要在0.016秒左右,NSTimer是无法实现的。所以要用到Core Animation的另一个timer,CADisplayLink

CADisplayLink的头文件中,我们可以看到它的使用方法跟NSTimer是十分类似的,其同样也是需要注册到RunLoop中,但不同于NSTimer的是,它在屏幕需要进行重绘时就会让RunLoop调用CADisplayLink指定的selector,用于准备下一帧显示的数据。而NSTimer是需要在上一次RunLoop整个完成之后才会调用制定的selector,所以在调用频率与上比NSTimer要频繁得多。

另外和NSTimer不同的是,NSTimer可以指定timeInterval,对应的是selector调用的间隔,但如果NSTimer触发的时间到了,而RunLoop处于阻塞状态,其触发时间就会推迟到下一个RunLoop。而CADisplayLink的timer间隔是不能调整的,固定就是一秒钟发生60次,不过可以通过设置其frameInterval属性,设置调用一次selector之间的间隔帧数。另外需要注意的是如果selector执行的代码超过了frameInterval的持续时间,那么CADisplayLink就会直接忽略这一帧,在下一次的更新时候再接着运行。

配置 RunLoop

在创建CADisplayLink的时候,我们需要指定一个RunLoop和RunLoopMode,通常RunLoop我们都是选择使用主线程的RunLoop,因为所有UI更新的操作都必须放到主线程来完成,而在模式的选择就可以用NSDefaultRunLoopMode,但是不能保证动画平滑的运行,所以就可以用NSRunLoopCommonModes来替代。但是要小心,因为如果动画在一个高帧率情况下运行,会导致一些别的类似于定时器的任务或者类似于滑动的其他iOS动画会暂停,直到动画结束。

private func setup() {_displayLink = CADisplayLink(target: self, selector: #selector(update))_displayLink?.isPaused = true_displayLink?.add(to: RunLoop.main, forMode: .commonModes)
}复制代码

实现不同的字符变换动画

在成功建立CADisplayLink计时器后,就可以着手对字符串进行各类动画操作了。在这里我们会使用NSAttributedString来实现效果

setupAnimatedText(from labelText: String?)这个方法中,我们需要使用到两个数组,一个是durationArray,一个是delayArray,通过配置这两个数组中的数值,我们可以实现对字符串中各个字符的出现时间出现时长的控制。

打字机效果的配置

  • 每个字符出现所需时间相同
  • 下一个字符等待上一个字符出现完成后再出现
  • 通过修改NSAttributedStringKey.baselineOffset调整字符位置
case .typewriter:attributedString.addAttribute(.baselineOffset, value: -label.font.lineHeight, range: NSRange(location: 0, length: attributedString.length))let displayInterval = duration / TimeInterval(attributedString.length)for index in 0..<attributedString.length {durationArray.append(displayInterval)delayArray.append(TimeInterval(index) * displayInterval)}复制代码

闪烁效果的配置

  • 每个字符出现所需时间随机
  • 确保所有字符能够在duration内均完成出现
  • 修改NSAttributedStringKey.foregroundColor透明度来实现字符的出现效果
case .shine:attributedString.addAttribute(.foregroundColor, value: label.textColor.withAlphaComponent(0), range: NSRange(location: 0, length: attributedString.length))for index in 0..<attributedString.length {delayArray.append(TimeInterval(arc4random_uniform(UInt32(duration) / 2 * 100) / 100))let remain = duration - Double(delayArray[index])durationArray.append(TimeInterval(arc4random_uniform(UInt32(remain) * 100) / 100))}
复制代码

渐现效果的配置

  • 每个字符出现所需时间渐减
  • 修改NSAttributedStringKey.foregroundColor透明度来实现字符的出现效果
case .fade:attributedString.addAttribute(.foregroundColor, value: label.textColor.withAlphaComponent(0), range: NSRange(location: 0, length: attributedString.length))let displayInterval = duration / TimeInterval(attributedString.length)for index in 0..<attributedString.length  {delayArray.append(TimeInterval(index) * displayInterval)durationArray.append(duration - delayArray[index])}
复制代码

完善每一帧的字符串更新效果

接下来就需要完善刚才在CADisplayLink中配置的update方法了,在这个方法中我们会根据我们刚才配置的两个数组中的相关数据对字符串进行变换。

核心代码

  • 通过开始时间当前时间获取动画进度
  • 根据字符位置对应duationArraydelayArray中的数据
  • 根据durationArraydelayArray中的数据计算当前字符的显示进度
var percent = (CGFloat(currentTime - beginTime) - CGFloat(delayArray[index])) / CGFloat(durationArray[index])
percent = fmax(0.0, percent)
percent = fmin(1.0, percent)
attributedString.addAttribute(.baselineOffset, value: (percent - 1) * label!.font.lineHeight, range: range)
复制代码

随后便可以将处理完的NSAttributedString返回给label进行更新

番外:利用正弦函数实现波纹进度

波纹路径

首先介绍一下正弦函数:y = A * sin(ax + b)

  • 在 x 轴方向平移 b 个单位(左加右减)
  • 横坐标伸长(0 < a < 1)或者缩短(a > 1) 1/a 倍
  • 纵坐标伸长(A > 1)或者缩短(0 < A < 1)A 倍

在简单了解了这些知识后,我们回到wavePath()方法中,在这个方法我们使用正弦函数来绘制一段UIBezierPath

let originY = (label.bounds.size.height + label.font.lineHeight) / 2
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: _waveHeight!))
var yPosition = 0.0
for xPosition in 0..<Int(label.bounds.size.width) {yPosition = _zoom! * sin(Double(xPosition) / 180.0 * Double.pi - 4 * _translate! / Double.pi) * 5 + _waveHeight!path.addLine(to: CGPoint(x: Double(xPosition), y: yPosition))
}
path.addLine(to: CGPoint(x: label.bounds.size.width, y: originY))
path.addLine(to: CGPoint(x: 0, y: originY))
path.addLine(to: CGPoint(x: 0, y: _waveHeight!))
path.close()
复制代码

波纹高度与动画的更新

  • 随着进度高度不断升高
  • 随着进度波纹不断波动

CADisplayLink注册的update的方法中,我们对承载了波纹路径的Layer进行更新

_waveHeight! -= duration / Double(label!.font.lineHeight)
_translate! += 0.1
if !_reverse {_zoom! += 0.02if _zoom! >= 1.2 {_reverse = true}
} else {_zoom! -= 0.02if _zoom! <= 1.0 {_reverse = false}
}
shapeLayer.path = wavePath()
复制代码

结语

以上就是我对CADisplayLink的一些运用,其实它的使用方法还有很多,可以利用它实现更多更复杂而精美的动画,同时希望各位如果有更好的改进也能与我分享。

如果你喜欢这个项目,欢迎到GitHub上给我一个star。

参考

  • RQShineLabel
  • Apple Developer Document - CADisplayLink
  • iOS核心动画高级技巧

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

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

相关文章

《ASP.NET Core 6框架揭秘》实例演示[27]:ASP.NET Core 6 Minimal API的模拟实现

Minimal API仅仅是在基于IHost/IHostBuilder的服务承载系统上作了小小的封装而已&#xff0c;它利用WebApplication和WebApplicationBuilder这两个类型提供了更加简洁的API&#xff0c;同时提供了与现有API的兼容。[本文节选《ASP.NET Core 6框架揭秘》第17章]一、基础模型二、…

Mysql的关联查询语句

一 内连接( inner join&#xff09; 1、多表中同时符合某种条件的数据记录的集合 (取两表公共部分) 2、inner join 可以缩写成 join 例如: select * from A,B WHERE A.idB.id 或者 select * from A inner join B on A.idB.id 内连接分为三类:{ &#xff08;1&#xff0…

高性能Server---Reactor模型

无处不在的C/S架构 在这个充斥着云的时代,我们使用的软件可以说99%都是C/S架构的&#xff01; 你发邮件用的Outlook,Foxmail等你看视频用的优酷&#xff0c;土豆等你写文档用的Office365,googleDoc&#xff0c;Evernote等你浏览网页用的IE,Chrome等(B/S是特殊的C/S)……C/S架构…

计算机控制系统的试题,计算机控制系统练习题(1)

21. 给出多通道复用一个D/A转换器的原理示意图。 答&#xff1a;22. 什么是信号重构&#xff1f;答&#xff1a;把离散信号变为连续信号的过程&#xff0c;称为信号重构&#xff0c;它是采样的逆过程。23. 写出零阶保持器的传递函数&#xff0c;引入零阶保持器对系统开环传递函…

springmvc_3(将数据放入map中)

jsp页面 结果 转载于:https://www.cnblogs.com/mohehpc/p/6491376.html

怎样用原生js配合css的transition写个无缝滚动

之所以想要写原生js配合css转换的无缝滚动&#xff0c;是因为之前在简书上看到一哥们写的一篇文章&#xff0c;说是在网上找了一堆js配合css transition属性写的轮播插件&#xff0c;可惜没有无缝的效果&#xff0c;结果他用原生js重写了一个可以无缝滚动的。好吧&#xff0c;我…

聊聊策略模式

1、简介策略模式就是把各个平等的具体实现进行抽象、封装成为独立的算法类&#xff0c;然后通过上下文和具体的算法类来进行交互。各个策略算法都是平等的&#xff0c;地位是一样的&#xff0c;正是由于各个算法的平等性&#xff0c;所以它们才是可以相互替换的。虽然我们可以动…

小学计算机课每周几节,小学信息技术课时多少

满意答案小学信息技术课程标准一、课程任务和教学目标中小学信息技术课程的主要任务是&#xff1a;培养学生对信息技术的兴趣和意识&#xff0c;让学生了解和掌握信息技术基本知识和技能&#xff0c;了解信息技术的发展及其应用对人类日常生活和科学技术的深刻影响。通过信息技…

张旭升20162329 2006-2007-2 《Java程序设计》第一周学习总结

20162329 2006-2007-2 《Java程序设计》第一周学习总结 教材学习内容总结 通过打书上的代码熟悉了Java编程的基本过程 教材学习中的问题和解决过程 1.因为我的虚拟机不可用所以我在Windows中安装了bash和git&#xff0c;但是由于Windows下bash中没有中文而且我英语又不是很好所…

《图解 HTTP》读书笔记(未完待续)

ARP 协议&#xff08;Address Resolution Protocol&#xff09;一种以解析地址的协议&#xff0c;根据通信双方的 IP 地址就可以查出对应的 MAC 地址。MAC&#xff08; Media Access Control Address&#xff09;地址是指网卡所属的固定的地址MIME&#xff0c;多部分对象集合&a…

SQL查询的安全方案

1.使用预处理语句防sql注入 2.写入数据库的数据要进行特殊字符转义 3.错误信息不返回给用户,记录到日志 4.定期做数据备份 5.不给查询用户root权限,合理分配权限 6.关闭远程访问数据库权限 7.修改root口令,不使用默认口令,使用较复杂口令 8.删除多余的用户 9.改变root用户的名称…

.NET 实现启动时重定向程序运行路径及 Windows 服务运行模式部署

日常工作中有时候会遇到需要将程序直接在服务器上运行&#xff0c;而不依赖于 IIS 托管的情况&#xff0c;直接运行有两种方式&#xff0c;一种是部署为 服务模式&#xff0c;另一种则是 直接启动 .NET 发布之后的 exe 文件以 控制台模式运行&#xff0c;控制台模式运行主要问题…

iOS runtime实战应用:关联对象

在开始之前建议先阅读iOS runtime的基础理解篇&#xff1a;iOS内功篇&#xff1a;runtime 有筒子在面试的时候&#xff0c;遇到这样一个问题&#xff1a;“如何給NSArray添加一个属性&#xff08;不能使用继承&#xff09;”&#xff0c;筒子立马蒙逼了&#xff0c;不能用继承&…

黑龙江科技大学计算机考研复试科目,2020年黑龙江科技大学计算机应用技术考研经验分享...

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼育明考研备考策略随着IT业的迅猛发展&#xff0c;各高校计算机专业报名火爆&#xff0c;甚至文科学生跨专业报考时都会选择计算机。计算机专业竞争日趋激烈&#xff0c;那么如何在充分发挥公共科目优势的同时&#xff0c;尽量缩小专…

Mysql数据库安全性问题【防注入】

一、SQL注入实例 后台的插入语句代码&#xff1a; $unsafe_variable $_POST[user_input]; mysql_query("INSERT INTO table (column) VALUES ($unsafe_variable)"); 当POST的内容为&#xff1a; value); DROP TABLE table;--以上的整个SQL查询语句变成&#xff1…

Unexpected end of JSON input while parsing near错误解决方式(网上的方法)

原本是想创建一个create-react-app来着&#xff0c;但是在创建的中间会出现Unexpected end of JSON input while parsing near... 的错误。 在网上找到了一些方法&#xff0c;首先是清空npm的缓存。 npm cache clean --force 氮素&#xff0c;然并卵。near后面的内容变化了一下…

解决Qt5 Creator无法切换输入法(fcitx),Ubuntu中不能使用搜狗输入法录入汉字问题...

2016年6月8日修正&#xff0c;ubuntu 16.04 Qt5.7.0 以及 Qt5.6.1均测试通过在Qt5.3之前&#xff0c;我发布过解决办法 解决Qt5 Creator无法切换输入法&#xff08;fcitx&#xff09;&#xff0c;不能录入汉字问题&#xff0c;Qt5.4以及Qt5.5&#xff0c;旧办法失效&#xff0c…

目前市场上用于个人计算机的硬盘尺寸是,第5章-硬盘(计算机组装与维护).docx

ADDIN CNKISM.UserStyle一、选择题1.磁盘存储器的主要技术指标有多项&#xff0c;下面不属于硬盘指标的是( )。A.存储容量B.单碟容量C.转速D.带宽2.硬盘的平均寻道时间通常以毫秒为单位测量&#xff0c;是指( )。A.磁头从一个柱面移到另一个随机距离远的柱面所需的平均时间B.…

Xmemcached学习笔记一(安装memcached)

memcached有三种java客户端 第一种&#xff1a;Com.danga 包下面的memcached&#xff0c;需引入jar(本人用的是memcached-2.5.2.jar 文末附上附件需要的可以下载) 第二种&#xff1a;spyMemcached 第三种&#xff1a;XMemcached 据说第三种是使用最简单&#xff0c;最好用的&a…

WrapPanel 实现虚拟化

WrapPanel 实现虚拟化控件名&#xff1a;VirtualizingWrapPanel作者&#xff1a;WPFDevelopersOrg原文链接&#xff1a; https://github.com/WPFDevelopersOrg/WPFDevelopers框架使用大于等于.NET40&#xff1b;Visual Studio 2022;项目使用 MIT 开源许可协议&#xff1b;众…