swift简单弹幕例子,仿哔哩哔哩

先看例子
在这里插入图片描述
每个弹幕的速度都是不一样的,支持弹幕整体开始暂停。
如果弹幕实在是太多了,有个缓冲队列,不停的重试能否显示,保证文字都能显示全,并且每条都能显示。

实现是基于 CADisplayLink 实现的,如此来说比直接搞个定时器来计算偏移丝滑,简单的平移动画如下:

import UIKitclass ViewController: UIViewController {let squareView = UIView()override func viewDidLoad() {super.viewDidLoad()// 创建 CADisplayLink 对象let displayLink = CADisplayLink(target: self, selector: #selector(update))// 将视图控制器添加到 displayLink 中displayLink.add(self, for: .common)// 设置视图属性squareView.frame = CGRect(x: 50, y: 50, width: 100, height: 100)squareView.backgroundColor = UIColor.red.withAlphaComponent(0.5)view.addSubview(squareView)}@objc func update(_ displayLink: CADisplayLink) {// 在每一帧更新时移动视图squareView.frame.origin.x += 5}
}

在这个基础版本上稍微改了改就变成如下代码:

import Foundation
import UIKitclass XDanMu {var row: Int = 0var label: UILabel = UILabel()var speed: CGFloat = 0var isMe: Bool = false
}class XDanMuView: UIView {var displayLink: CADisplayLink?var lineHeight: CGFloat = 26var gap: CGFloat = 20var minSpeed: CGFloat = 1var maxSpeed: CGFloat = 2var isPause: Bool = falsevar danmus: [XDanMu] = []var danmuQueue: [(String, Bool)] = []var timer: Timer?func start() {displayLink = CADisplayLink(target: self, selector: #selector(update))displayLink?.add(to: RunLoop.current, forMode: .common)timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(handleDanMuQueue), userInfo: nil, repeats: true)}@objc func handleDanMuQueue() {if danmuQueue.isEmpty {return}let danmu = danmuQueue.removeFirst()addDanMu(text: danmu.0, isMe: danmu.1)}@objc func addDanMu(text: String, isMe: Bool) {let danmu = XDanMu()danmu.label.frame.origin.x = self.frame.size.widthdanmu.label.text = textdanmu.label.sizeToFit()if isMe {danmu.label.layer.borderWidth = 1}var linelasts: [XDanMu?] = []let rows: Int = Int(self.frame.size.height / lineHeight)for _ in 0..<rows {linelasts.append(nil)}for d in danmus {if d.row >= linelasts.count {break}if linelasts[d.row] != nil {let endx = danmu.label.frame.origin.xlet targetx = linelasts[d.row]!.label.frame.origin.xif endx > targetx {linelasts[d.row] = d}} else {linelasts[d.row] = d}}var isMatch = falsefor index in 0..<linelasts.count {if let d = linelasts[index] {let endx = d.label.frame.origin.x + d.label.frame.size.width + gapif endx < self.frame.size.width {danmu.row = indexvar ms = self.frame.size.width / endx * d.speedms = CGFloat.minimum(ms, maxSpeed)danmu.speed = CGFloat.random(in: minSpeed...ms)isMatch = truebreak}} else {danmu.row = indexdanmu.speed = CGFloat.random(in: minSpeed...maxSpeed)isMatch = truebreak}}if isMatch == false {danmuQueue.append((text, isMe))return}danmu.label.frame.origin.y = lineHeight * CGFloat(danmu.row)self.addSubview(danmu.label)self.danmus.append(danmu)}@objc func update(_ displayLink: CADisplayLink) {if isPause == true {return}// 在每一帧更新时移动视图for index in 0..<danmus.count {let danmu = danmus[index]danmu.label.frame.origin.x -= danmu.speedif danmu.label.frame.origin.x < -danmu.label.frame.size.width {danmu.label.removeFromSuperview()danmus.remove(at: index)break}}}
}

再找个需要使用的地方加入如下使用的代码,即可实现上图的效果

override func viewDidLoad() {super.viewDidLoad()var danmuView: XDanMuView = XDanMuView()danmuView.frame = .init(x: 0, y: 100, width: self.view.frame.size.width, height: self.view.frame.size.height - 200)self.view.addSubview(danmuView)// 配置项danmuView.minSpeed = 1danmuView.maxSpeed = 2danmuView.gap = 20danmuView.lineHeight = 30// 启动弹幕danmuView.start()// 启动一个定时器灌弹幕timer = Timer.scheduledTimer(timeInterval: 0.4, target: self, selector: #selector(addDanMu), userInfo: nil, repeats: false)
}@objc func addDanMu() {let interval = CGFloat.random(in: 0.3...1.0)Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(addDanMu), userInfo: nil, repeats: false)var text = ""for _ in 0...Int.random(in: 1...30) {text += "嘿"}for _ in 0...Int.random(in: 1...2) {danmuView.addDanMu(text: text, isMe: Bool.random())}
}

文本的字体自行根据需求修改,目前是没有增加样式跟颜色。
完整工程传送门
github
gitee

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

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

相关文章

工业以太网的发展历程与应用前景

工业以太网是在工业自动化和物联网领域广泛使用的通信网络&#xff0c;它具有应用广泛、价格低廉、通信速率高、软硬件产品丰富、应用支持技术成熟等优点&#xff0c;目前它已经在工业企业综合自动化系统中的资源管理层、执行制造层得到了广泛应用&#xff0c;并呈现向下延伸直…

Verilog 学习之路(三)——牛客刷题篇

1.输入序列连续的序列检测 题面 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kJH9kHFH-1690301233803)(https://s2.loli.net/2023/07/26/HJPXR2mhbaVCG6d.png)]思路 对于序列检测题目&#xff0c;常规的解法有两种&#xff1a;状态机法和序列缓存…

概率论和随机过程的学习和整理--番外16,N合1的合成问题的求平均个数,次数,阶数

目录 1 问题 2 用条件期望&#xff0c;求合成的次数 2.1 思路1 2.2 思路2 3 用条件期望&#xff0c;求合成的个数 3.1 令X表示用材料1往上合成时&#xff0c;合成材料2的个数 3.2 令Y表示用材料1往上合成时&#xff0c;合成材料3的个数 4 用条件期望&#xff0c;求合成…

基于express调用chatgpt文字流输出和有道智云语音合成

express是基于node.js的一个web框架&#xff0c;可以更加简洁的去创建一个后台服务&#xff0c;由于项目的需要&#xff0c;引入和typescript&#xff0c;经过几天的努力实现了chatgpt文字流输出有道智云语音合成的结合&#xff08;略有遗憾&#xff09;&#xff0c;下面我记载…

现行业供应链数字化转型难的最根本原因是什么?

由于各种原因&#xff0c;行业供应链的数字化转型可能充满挑战。然而&#xff0c;最根本的原因之一是供应链本身固有的复杂性和碎片化。以下是造成这一困难的一些关键因素&#xff1a; 1.缺乏标准化&#xff1a;供应链通常涉及多个组织、系统和流程&#xff0c;这些组织、系统…

删除主表 子表外键没有索引的性能优化

整个表147M&#xff0c;执行时一个CPU耗尽&#xff0c; buffer gets 超过1个G&#xff0c; 启用并行也没有用 今天开发的同事问有个表上的数据为什么删不掉&#xff1f;我看了一下&#xff0c;也就不到100000条数据&#xff0c;表上有外键&#xff0c;等了5分钟hang在那里&…

网络安全系统教程+学习路线(自学笔记)

一、什么是网络安全 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防两面…

Upload-Labs通关

问题 记录BUG—在uploadlabs第三关中—关于phpstudy中修改httpd.conf依旧无法解析.php3d等问题_upload第三关常见错误_dfzy$_$的博客-CSDN博客 我们首先先来了解一下什么是文件上传 首先 很简单 文件上传就是 需要用户进行上传文件 图片或视频等信息但是如果用户恶意上传木马…

Kafka的TimingWheel

Kafka的TimingWheel是Kafka中的一个时间轮实现,用于管理和处理延迟消息。时间轮是一种定时器的数据结构,可以高效地管理和触发定时事件。 在Kafka中,TimingWheel用于处理延迟消息的重试。当Kafka生产者发送消息到Kafka集群,但由于某些原因导致消息发送失败,生产者会将这些…

C++中的数学问题---进制转换

二进制转十六进制 string binToHex(string bin){string hex"";if(bin.size()%4!0){for(int i0;i<(4-bin.size()%4);i){bin"0"bin;}}for(int i0;i<bin.size();i4){string tmpbin.substr(i,4);bitset<4>b(tmp);hexb.to_ulong()<10?char(b.t…

解决报错:Can‘t connect to HTTPS URL because the SSL module is not available.

本人今天准备打开安装一个label-studio包&#xff0c;试了很多次&#xff0c;接连报如下错误&#xff0c;因此我就去找了一些解决方案&#xff0c;现在总结如下&#xff1a; 1、报错信息如下 2、解决方案如下&#xff1a; github上有对应的解决方案&#xff0c;链接&#xff…

操作系统18:磁盘I/O速度、磁盘可靠性、数据一致性

目录 1、提高磁盘I/O速度的途径 &#xff08;1&#xff09;磁盘高速缓存(Disk Cache) 1.1 - 数据交付(Data Delivery)方式 1.2 - 置换算法 1.3 - 周期性地写回磁盘 &#xff08;2&#xff09;提高磁盘I/O速度的其它方法 2.1 - 提前读 2.2 - 延迟写 2.3 - 优化物理块的…

安全渗透--正则表达式

什么是正则表达式&#xff1f; 正则表达式是一组由字母和符号组成的特殊文本&#xff0c;它可以用来从文本中找出满足你想要的格式的句子。 一个正则表达式是一种从左到右匹配主体字符串的模式。 “Regular expression”这个词比较拗口&#xff0c;我们常使用缩写的术语“regex…

面试算法题

1 使用栈实现队列 #include <iostream> #include <stack> class MyQueue { public:MyQueue() {}void push(int x){in.push(x); // 直接将元素push入in栈}int pop(){int data peek(); // 先查一遍&#xff0c;就是更新一遍out栈out.pop();return data;}// 查找队…

吴恩达ChatGPT《LangChain Chat with Your Data》笔记

文章目录 1. Introduction2. Document Loading2.1 Retrieval Augmented Generation&#xff08;RAG&#xff09;2.2 Load PDFs2.3 Load YouTube2.4 Load URLs2.5 Load Notion 3. Document Splitting3.1 Splitter Flow3.2 Character Splitter3.3 Token Splitter3.4 Markdown Spl…

数据库技术的基本概念、原理、方法和技术

一、基本概念 1. DB&#xff1a;数据库&#xff0c;保存一组有组织的数据的容器&#xff1b; 2. DBMS: 数据库管理系统&#xff0c;又称为数据库软件&#xff08;数据库产品)&#xff0c;用于管理DB中的数据&#xff1b; 3. SQL: 结构化查询语言&#xff0c;用于和DBMS通信的…

Ubuntu-解决包依赖关系

Ubuntu-解决包依赖关系的办法 安装软件包的时候&#xff0c;有时会遇到类似下图的依赖问题&#xff0c;无法正常安装&#xff0c;下面提供三种方法解决依赖问题。 1.可以尝试用下面方法处理依赖问题&#xff0c;紧跟前一条安装命令后面输入下面命令&#xff0c;然后再执行安装…

数据结构:计数排序(详解)

思路详解&#xff1a; 1 找到数组中的最大值、最小值 2 开辟一个统计每个数据出现次数的数组&#xff08;总个数是最大值-最小值1&#xff0c;因为下标范围是0~最大值-最小值&#xff0c;闭区间统计个数要1&#xff09; 3 遇到一个元素&#xff0c;在此元素-最小值作为下标的…

TSN -促进IT/OT 融合的网络技术

时间敏感网络&#xff08;tsn&#xff09;技术是IT/OT 融合的一项关键的基础网络技术&#xff0c;它实现了在一个异构网络中&#xff0c;实现OT的实时数据和IT系统的交互数据的带宽共享。 TSN允许将经典的高确定性现场总线系统和IT应用&#xff08;如大数据传输&#xff09;的功…

java 页面html常用写法总结

​(注意&#xff1a;本文章默认base html中已经引入bootstrap.min.css、style.css等css样式) input &#xff1a;输入标签 <#input required"必填" id"cycle" name"周期" underline"true" style"width:75%" itype&quo…