iOS文字滚动:使用CATextLayer实现的跑马灯(附源码)

server/2025/2/6 11:51:31/

引言

在 iOS 开发中,跑马灯效果(Marquee Effect)是一种常见的文本滚动效果,广泛应用于广告展示、动态消息栏、通知推送等场景。通过跑马灯效果,我们能够以流畅的方式展示超出屏幕范围的文本,提升用户体验。

通常,在 iOS 中实现跑马灯效果,我们可能会想到UILabel。然而,虽然UILabel提供了丰富的文本样式支持,它在动画和性能方面却有一定局限性。特别是在需要自定义动画效果和处理高性能的场景中,UILabel并不是最理想的选择。

此时,CATextLayer就成为了一个更灵活的替代方案。CATextLayer是一个低级的图层类,继承自CALayer,它专注于文本渲染,并且可以与 Core Animation 配合,实现高效且平滑的动画效果。相比于UILabel,CATextLayer更加轻量,且能更精确地控制动画和渲染性能,因此非常适合用于跑马灯这类需要高效动画渲染的场景。

本文将通过一个实际的例子,介绍如何使用CATextLayer实现一个简洁、流畅的跑马灯效果,帮助你在 iOS 项目中灵活运用这一技术。

CATextLayer基础

CATextLayer是Core Animation框架中的一种特殊图层(CALayer的子类),专门用于渲染文本。它与UILabel的最大区别在于,它并不直接处理用户交互和文本样式的布局,而是通过图层的方式,专注于高效的文本渲染和动画效果。

在Core Animation中,CATextLayer被设计为一个性能高效的图层,能够承载大量的文本内容并进行平滑的动画。与UILabel类不同,CATextLayer主要用于显示文本,而不涉及复杂的布局计算和视图管理,因此它非常适合用于需要高效渲染的场景,比如动画、动态图文等。

CATextLayer的主要特点:

  1. 专注于文本渲染:CATextLayer不像UILabel那样支持复杂的用户交互和动态布局,它主要负责在屏幕上渲染文本,减少了多余的开销。
  2. 高性能的文本绘制:由于其直接依赖于Core Animation,CATextLayer在渲染性能上笔UILabel更加优秀,特别是在处理大量文本和需要高帧率动画时,能够提供更平滑的效果。
  3. 支持基本的文本属性:CATextLayer支持字体、大小、颜色、对齐方式等基本的文本属性,但它并不完全支持 NSAtrributedString中的所有样式。这使得它非常适合需要简洁文本样式和高效动画的场景。
  4. 与Core Animation配合使用:CATextLayer天生与Core Animation配合,能够与其他图层(例如CALayer)进行协调,创建复杂的动画效果,如滚动、变换、透明度变化等。利用CATextLayer,可以实现诸如跑马灯、动态通知等动画效果。
  5. 不直接管理布局:与UILabel不同CATextLayer不负责复杂的自动布局或响应交互,它仅在指定的区域内渲染文本,适合用于自定义动画和轻量级显示。

通过CATextLayer,开发者可以更加灵活地控制文本的渲染和动画,尤其在需要高效且流畅显示大段文本的场景中,CATextLayer 提供了比 UILabel 更加高效的方案。

实现步骤

为了让我们自定义的跑马灯文字组件使用起来和普通的UILabel一样,可以直接继承自UILabel来构建自定义的文字自动滚动组件。在自定义的组件内部创建一个CATextLayer图层用来承载文字和执行动画。

准备工作

为了实现滚动效果,首先我们需要当前文本展示完成后所需的组件宽度和滚动速度,然后根据宽度再来创建特殊的文字图层CATextLayer以及执行滚动动画。

    /// 文字图层private var marqueeLayer: CATextLayer?/// 速度private var speed: CGFloat = 50.0
    private func setupTextLayer() {self.layer.masksToBounds = true// 清理图层clearLayer()// 计算文字宽度,优先考虑富文本var attributedText = self.attributedTextif attributedText == nil {attributedText = NSAttributedString(string: text ?? "", attributes: [.font: font, .foregroundColor: textColor])}let textWidth = attributedText?.boundingRect(with: CGSize(width: CGFloat(MAXFLOAT), height: frame.height), options: .usesLineFragmentOrigin, context: nil).width ?? 0guard let attributedText = attributedText else {return}// 添加文字图层addMarqueeTextLayer(attributedString: attributedText, width: textWidth)if textWidth <= self.bounds.width {return}// 添加动画addMarqueeAnimation(width: textWidth, textLayer: marqueeLayer!)}override func layoutSubviews() {super.layoutSubviews()if self.bounds.size.width > 0 {setupTextLayer()}}
  1. 声明了一个CATextLayer特殊图层。
  2. 定义了滚动的速度。
  3. setupTextLayer()方法中计算文本的宽度,来决定整个文字图层是否需要滚动。
  4. 为了适应约束布局,重写layoutSubviews()方法,并在组件已经有了宽度的时候重新执行setupTextLayer()方法。

创建CATextLayer

当获取到文本内容,计算出文本宽度之后就可以根据内容和宽度来创建CATextLayer添加到UILabel的图层之上并设置布局。

    /// 添加文字图层/// - Parameters:///  - attributedString: 富文本///  - width: 文字宽度private func addMarqueeTextLayer(attributedString:NSAttributedString,width:CGFloat) {let textLayer = CATextLayer()textLayer.string = attributedStringtextLayer.contentsScale = UIScreen.main.scaletextLayer.alignmentMode = .lefttextLayer.frame = CGRect(x: 0, y: 0, width: width, height: frame.height)layer.addSublayer(textLayer)self.marqueeLayer = textLayerprint("string:\(attributedString)")textLayer.backgroundColor = UIColor.red.cgColor}
  1. 创建图层并设置图层的文字内容。
  2. 设置contentsScale这一步十分关键,否则在Retina会出现模糊。
  3. 设置frame,x和y分别从0开始,宽度为文字总宽度,高度为组件高度。
  4. 设置UILabel的原本颜色为透明色,目的是隐藏UILabel原来的显示内容。

添加动画

CATextLayer创建完成之后,我们只需要让它的最左端从组件的最右端开始执行动画,直到CATextLayer图层的最右端与组件的最左端对齐为止,为此我们需要计算起始位置和结束位置。

    /// 添加图层动画/// - Parameters:/// - width: 文字宽度/// - textLayer: 文字图层private func addMarqueeAnimation(width: CGFloat, textLayer: CATextLayer) {//添加动画let startOffset = bounds.width + width / 2.0let endOffset = -width/2.0let duration = Double(width) / Double(speed)let animation = CAKeyframeAnimation(keyPath: "position.x")animation.values = [startOffset, endOffset]animation.keyTimes = [0, 1]animation.duration = durationanimation.repeatCount = .infinityanimation.isRemovedOnCompletion = falsetextLayer.add(animation, forKey: "marqueeScroll")}
  1. 我们针对图层的position.x执行关键帧动画,那么它的起点位置应该是组件宽度+文字宽度/2。
  2. 而结束为止应该是负的文字宽度。
  3. 根据速度计算动画时长。
  4. 设置重复次数无限大,让动画一直循环。

清理图层

最后还有一个重要的方法,清理特殊图层。

    /// 清理图层private func clearLayer() {self.marqueeLayer?.removeAllAnimations()self.marqueeLayer?.removeFromSuperlayer()self.marqueeLayer = nil}

使用示例

在使用时,我们只需要像普通UILabel一样使用,支持绝对布局和相对布局。

    /// 添加跑马灯文字private func addMarqueeLabel() {let marqueeLabel = PHMarqueeLabel(frame: CGRect(x: 100.0, y: 100.0, width: 200.0, height: 40.0))marqueeLabel.text = "今天是一个好天气,适合出去玩耍"marqueeLabel.textColor = .whitemarqueeLabel.font = UIFont.systemFont(ofSize: 20)marqueeLabel.backgroundColor = .orangeview.addSubview(marqueeLabel)}
        /// 添加跑马灯文字private func addMarqueeLabel() {let marqueeLabel = PHMarqueeLabel(frame: .zero)let text = "今天是一个好天气,适合出去玩耍"let attributedText = NSMutableAttributedString(string: text)attributedText.addAttributes([.foregroundColor: UIColor.blue], range: NSRange(location: 0, length: 2))attributedText.addAttributes([.foregroundColor: UIColor.green], range: NSRange(location: 2, length: 2))marqueeLabel.attributedText = attributedTextmarqueeLabel.font = UIFont.systemFont(ofSize: 20)marqueeLabel.backgroundColor = .orangeview.addSubview(marqueeLabel)marqueeLabel.snp.makeConstraints { make inmake.top.equalTo(100.0)make.leading.trailing.equalToSuperview().inset(100.0)}}

效果如下:

结语

通过本文,我们探讨了如何使用 CATextLayer 实现一个高效流畅的跑马灯效果。相比于 UILabel,CATextLayer在渲染性能上更具优势,特别是在需要动态更新和动画效果时,它能够提供更加平滑的用户体验。尽管 CATextLayer 支持的文本样式有限,但对于一些简单的文本显示需求,尤其是高效动画渲染,它无疑是一个理想的选择。

在实际开发中,使用 CATextLayer 实现跑马灯效果,能够帮助我们节省性能开销,减少无谓的视图层级,同时通过 Core Animation 提供流畅的视觉体验。无论是在广告轮播、消息通知还是动态标签的场景中,CATextLayer 都能够发挥出色的表现。

当然,CATextLayer 也并非万能,对于需要高度自定义富文本样式的场景,我们仍然可以结合 UILabel 或其他控件来实现更复杂的效果。但对于大多数简洁、流畅的滚动效果,CATextLayer 是一个值得推荐的解决方案。

希望通过本文的介绍,能够帮助你在项目中更好地使用 CATextLayer来实现跑马灯效果,提升动画表现与用户体验。

https://download.csdn.net/download/weixin_39339407/90341158https://download.csdn.net/download/weixin_39339407/90341158


http://www.ppmy.cn/server/165400.html

相关文章

【怎么用系列】短视频戒除-1-对推荐算法进行干扰

如今推荐算法已经渗透到人们生活的方方面面&#xff0c;尤其是抖音等短视频核心就是推荐算法。 【短视频的危害】 1> 会让人变笨&#xff0c;慢慢让人丧失注意力与专注力 2> 让人丧失阅读长文的能力 3> 让人沉浸在一个又一个快感与嗨点当中。当我们刷短视频时&#x…

Linux+Docer 容器化部署之 Shell 语法入门篇 【Shell 替代】

&#x1f380;&#x1f380;Shell语法入门篇 系列篇 &#x1f380;&#x1f380; LinuxDocer 容器化部署之 Shell 语法入门篇 【准备阶段】LinuxDocer 容器化部署之 Shell 语法入门篇 【Shell变量】LinuxDocer 容器化部署之 Shell 语法入门篇 【Shell数组与函数】LinuxDocer 容…

基于PyQt5打造的实用工具——PDF文件加图片水印,可调大小位置,可批量处理!

01 项目简介 &#xff08;1&#xff09;项目背景 随着PDF文件在信息交流中的广泛应用&#xff0c;用户对图片水印的添加提出了更高要求&#xff0c;既要美观&#xff0c;又需高效处理批量文件。现有工具难以实现精确调整和快速批量操作&#xff0c;操作繁琐且效果不理想。本项…

【JavaEE】_MVC架构与三层架构

目录 1. MVC架构 2. 三层架构 3. MVC架构与三层架构的对比 3.1 MVC与三层架构的对比 3.2 MVC与三层架构的共性 1. MVC架构 在前文已介绍关于SpringMAC的设计模式&#xff0c;详见下文&#xff1a; 【JavaEE】_Spring Web MVC简介-CSDN博客文章浏览阅读967次&#xff0c;点…

拍照对比,X70 PRO与X90 PRO+的细节差异

以下是局部截图&#xff08;上X70P下X90PP&#xff09; 对比1 这里看不出差异。 对比2 X90PP的字明显更清楚。 对比3 中下的字&#xff0c;X90PP显然更清楚。

全流程安装DeepSeek开源模型

目录 配置要求安装Ollama选择大模型安装大模型对话备注 配置要求 我的电脑配置为&#xff1a; CPU&#xff1a;i7 12代 GPU&#xff1a;3080 内存&#xff1a;32g 磁盘&#xff1a;1T以上配置运行情况&#xff1a; 运行ollama run deepseek-r1:7b模型无压力&#xff0c;CPU/…

基于单片机的智能家居设计(论文+源码)

2.1需求分析 本次基于STM32的蓝牙智能家居&#xff0c;在功能上如下&#xff1a; 1.可以实现家居环境温湿度&#xff0c;烟雾浓度的检测。 2.可以将检测到的温湿度&#xff0c;烟雾浓度等信息通过蓝色传输到手机APP&#xff1b; 3.用户可以打开或者关闭风扇&#xff0c;以便…

Aosp 15 编译遇到问题排查

最近想编译aosp 15 的系统。特意从谷歌下载一份 <remote name"aosp"fetch".."review"https://android-review.googlesource.com/" /><default revision"refs/tags/android-15.0.0_r11"remote"aosp"sync-j"4…