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

news2024/12/28 5:03:43

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

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

import UIKit

class 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 UIKit

class XDanMu {
    var row: Int = 0
    var label: UILabel = UILabel()
    var speed: CGFloat = 0
    var isMe: Bool = false
}

class XDanMuView: UIView {
    var displayLink: CADisplayLink?
    
    var lineHeight: CGFloat = 26
    var gap: CGFloat = 20
    var minSpeed: CGFloat = 1
    var maxSpeed: CGFloat = 2
    var isPause: Bool = false
    
    var 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.width
        danmu.label.text = text
        danmu.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.x
                let targetx = linelasts[d.row]!.label.frame.origin.x
                if endx > targetx {
                    linelasts[d.row] = d
                }
            } else {
                linelasts[d.row] = d
            }
        }
        
        var isMatch = false
        for index in 0..<linelasts.count {
            if let d = linelasts[index] {
                let endx = d.label.frame.origin.x + d.label.frame.size.width + gap
                if endx < self.frame.size.width {
                    danmu.row = index
                    var ms = self.frame.size.width / endx * d.speed
                    ms = CGFloat.minimum(ms, maxSpeed)
                    danmu.speed = CGFloat.random(in: minSpeed...ms)
                    isMatch = true
                    break
                }
            } else {
                danmu.row = index
                danmu.speed = CGFloat.random(in: minSpeed...maxSpeed)
                isMatch = true
                break
            }
        }
        
        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.speed
            if 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 = 1
    danmuView.maxSpeed = 2
    danmuView.gap = 20
    danmuView.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.coloradmin.cn/o/779085.html

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

相关文章

mac使用教程【快速从windows切换为mac,mac快捷键合集】

mac使用教程 1. 安装brew并通过brew安装git 1.1 安装brew 打开终端输入如下命令&#xff1a; % /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"选择对应的镜像进行安装 # 例如&#xff1a;输入5&#xff…

了解 3DS MAX 3D摄像机跟踪设置:第 5部分

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. 创建陨石坑 步骤 1 启动 3ds Max 和 打开本教程最后一部分中保存的文件。 启动 3ds Max 步骤 2 删除所有占位符 从头开始创建陨石坑。 删除所有占位符 步骤 3 创建具有“长度”的平面 段和宽度段各…

如何创建vue2,vue3项目

前提需安装node.js和Vue CLI node.js:https://nodejs.org/zh-cn Vue CLI&#xff1a; npm install -g vue/cli 如何创建一个vue2项目 &#xff08;1&#xff09; 使用cmd终端直接创建 进入到vue项目所创建的目录里&#xff08;我是直接创建在桌面上&#xff09; 选择vue2 …

【mysql】聚簇索引和非聚簇索引(B树和B+树)

博主简介&#xff1a;想进大厂的打工人博主主页&#xff1a;xyk:所属专栏: mysql 目录 一、索引分类 二、索引的数据结构 2.1 B树&#xff1a;改造二叉树 2.2 B树&#xff1a;改造B树 三、Mysql索引实现—InnoDB引擎 3.1 主键索引&#xff08;聚簇索引&#xff09; 3.2 …

如何利用plotly和geopandas根据美国邮政编码(Zip-Code)绘制美国地图

对于我自己来说&#xff0c;该需求源自于分析Movielens-1m数据集的用户数据&#xff1a; UserID::Gender::Age::Occupation::Zip-code 1::F::1::10::48067 2::M::56::16::70072 3::M::25::15::55117 4::M::45::7::02460 5::M::25::20::55455 6::F::50::9::55117我希望根据Zip-…

Python读写csv文件

简介 通过Python内置csv模块&#xff0c;可以读取和写入CSV&#xff08;逗号分隔值&#xff09;文件。 CSV是一种常见的文件格式&#xff0c;通常用于存储表格数据&#xff0c;每行数据由逗号分隔&#xff0c;每个字段可以用引号括起来。 测试文件内容如下 列号,年龄,姓名,性别…

香橙派Zero2安装wiringPi外设库

安装wiringOP库 直接在香橙派上下载 wiringOP 的代码 sudo apt update sudo apt install -y git git clone https://github.com/orangepi-xunlong/wiringOP 如果在香橙派上下载不下来&#xff0c;也可以在通过windows浏览器打开https://github.com/orangepi-xunlong/wiringOP …

【【高级程序设计语言C++】C++多态的概念及原理

1. 多态的概念2. 多态的定义及实现2.1. 多态的条件2.2. 虚函数2.3. 虚函数的重写2.4. 虚函数重写的两个例外2.5. C11的override和final2.6. 重载、重写、重定义的对比 3. 抽象类3.1. 概念3.2. 实现继承和接口继承的对比 4. 多态的原理4.1. 虚函数表4.2. 多态原理4.3. 动态绑定和…

(34)继电器开关

文章目录 前言 34.1 装有IOMCU的自动驾驶仪上的继电器引脚 34.2 通过任务规划器定义继电器引脚 34.3 飞行员控制继电器 34.4 任务控制继电器 34.5 任务规划器控制继电器 前言 "继电器"是自动驾驶仪上的一个数字输出引脚&#xff0c;可在 0V 和 3.3V 或 5V 之间…

商城-学习整理-基础-分布式组件(三)

目录 一、前言二、Spring Cloud&Spring Cloud Alibaba1、Spring Cloud 与Spring Cloud Alibaba简介2、为什么使用Spring Cloud Alibaba3、版本选择4、项目中的依赖 三、Spring Cloud Alibaba-Nacos作为注册中心1、Nacos1&#xff09;、下载 nacos-server2&#xff09;、启动…

【C++ 程序设计】第 1~9 章:常见知识点汇总

目录 一、C 语言简介 二、面向对象的基本概念 三、类和对象进阶 四、运算符重载 五、类的继承与派生 六、多态与虚函数 七、输入/输出流 八、文件操作 九、函数模板与类模板 一、C 语言简介 知识点名称内容C语言的发展简史★★1. C 语言是 C 语言的前身 &…

让GPT人工智能变身常用工具-上

1.密码生成器:GPT为您创建安全密码 想象GPT作为您的个人密码生成器,负责从头到尾为您创建复杂且安全的密码。您只需要告诉他您的密码需求,比如密码的长度,是否包含大写字母、小写字母、数字或特殊字符,他会立即为您生成一个复杂但经过深度设计的密码。 例子: 我希望您…

第八届中国开源年会(COSCon'23)启动!

*海报设计师&#xff1a;朱亿钦&#xff08;居居&#xff09; 一年一度的开源盛会&#xff0c;COSCon23 第八届中国开源年会&#xff0c;将于10月28~29日&#xff0c;在四川成都市高新区菁蓉汇召开&#xff01;本次大会的主题是&#xff1a;“开源&#xff1a;川流不息、山海相…

(css)滚动条样式

(css)滚动条样式 效果&#xff1a; /*滚动条整体样式*/ ::-webkit-scrollbar {width: 2px;/*高宽分别对应横竖滚动条的尺寸*/height: 10px; } ::-webkit-scrollbar-thumb {/*滚动条里面小方块*/border-radius: 10px;width: 2px;height: 60px;background: linear-gradient(0deg,…

leetcode 78. 子集

2023.7.22 本题为回溯系列的一道标准模板题。 如果将回溯问题抽象为一棵树的话&#xff0c;那么之前的组合、分割问题都是为了找到这棵树的叶子节点&#xff0c;而子集是要找到这棵树的所有节点。 然后要注意&#xff0c;子集是无序的&#xff0c;即{1&#xff0c;2}和{2&#…

幂等性设计与实现

文章目录 前言1.全局唯一ID1.1 前端防止重复提交1.2 token机制1.3 数据库表加唯一约束 2.幂等下 ABA问题 与乐观锁2.1 乐观锁2.2 如何解决ABA问题&#xff1f; 3.分布式锁和事务3.1 分布式锁&#xff1a;3.2. 分布式事务 前言 幂等性&#xff08;Idempotence&#xff09;是一个…

导航、开源镜像、Prompt ( AI 提示词 )、AI工具集、chatgpt镜像

1、导航 网站 众多网址导航中&#xff0c;哪个最好&#xff1f;理由是什么&#xff1f; &#xff1a;https://www.zhihu.com/question/19899559 除了百度&#xff0c;其他搜索引擎&#xff1a; 综合类搜索导航(Anywhere Anything)&#xff1a;http://lackar.com/aa/ 渗透师 导…

开源QianWei搭建音乐网站,并实现公网连接

开源QianWei搭建音乐网站&#xff0c;并实现公网连接 1、前言2、本地网页搭建2.1环境使用2.2 支持组建选择2.3 网页安装 3、本地网页发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4、公网访问测试5、结语 1、前言 音乐是我们生活和工作中不可或缺的调剂&#xff0c;它能让我们心…

二,jmeter的简介还有一些参数的说明

文章目录 一、jmeter简介及安装1. 简介2. 安装 二、jmeter设置语言三、jmeter文件路径说明四、编写jmeter脚本五、乱码的处理&#xff1a;1. 请求内容出现乱码处理方法2. 响应内容出现乱码处理方法 一、jmeter简介及安装 1. 简介 Apache 托管的开源java工具接口测试、自动化测…

Sublime Text 设置中文

文章目录 1. Subime Text 官网2. 中文设置 1. Subime Text 官网 https://www.sublimetext.com/ 2. 中文设置 打开 sublime&#xff0c;ctrl shift p&#xff0c;在对话框搜索 Install Package Control&#xff0c;点击 会弹出一个消息框&#xff0c;表示插件列表加载完成…