当前位置: 首页 > news >正文

Swift 技术 视频播放器滚动条(源码)

一直觉得自己写的不是技术,而是情怀,一个个的教程是自己这一路走来的痕迹。靠专业技能的成功是最具可复制性的,希望我的这条路能让你们少走弯路,希望我能帮你们抹去知识的蒙尘,希望我能帮你们理清知识的脉络,希望未来技术之巅上有你们也有我。

整个的原理在视频中已经说的很清楚了,不懂的话直接看视频是很明朗的.
在这里插入图片描述

在这里插入图片描述

前言

2023.09.03自定义封装一个视频拖拽的滚动条.需要满足一下的需求:
1.能拖拽快进
2.能点击快进
3.能预加载进度
4.显示当前播放进度
5.显示节点,点击跳到对应的节点

在这里插入图片描述

思路

该拖拽进度条底部是用来显示预加载进度的,使用系统自带的UIProgressView控件.最外面能拖拽播放进度的使用系统自带的UISlider控件.节点通过for循环创建按键出来的节点.这样有一个问题,当播放到节点的位置的时候,节点会挡住拖拽进度条的位置.所以拖拽进度条的原点,就用一张透明的图片(相当于不要了),然后创建一个按键在最前面,用一张圆形图片,挡住原来UISlider控件的原点,跟随滑动,播放的个过程

在这里插入图片描述
可以看到上面的图片就是用一个自定义的按键挡住了原来UISlider控件的原点.

重点

下面说一下整个封装的几个关键地方.

点击滚动条
在这里插入图片描述
算出point值的公式:
在这里插入图片描述

let point = ((pointTapped.x - positionOfSlider.x) * CGFloat(slider.maximumValue) / widthOfSlider)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
全部代码

//
//  PlayProgressView.swift
//  SwiftDemol
//
//  Created by 冯汉栩 on 2023/8/21.
//import UIKitclass PlayProgressView: UIView {// 预加载进度lazy var progress: UIProgressView = {let progress = UIProgressView()progress.progressTintColor = UIColor(red: 169.0/255.0, green: 169.0/255.0, blue: 169.0/255.0, alpha: 1.0)//深灰色progress.trackTintColor = UIColor(red: 228.0/255.0, green: 228.0/255.0, blue: 228.0/255.0, alpha: 1.0)//浅灰色return progress}()// 进度条 clearn_point   slider_pointer_normallazy var slider: UISlider = { [weak self] inlet slider  = UISlider()slider.minimumTrackTintColor = UIColor(red: 74.0/255.0, green: 155.0/255.0, blue: 234.0/255.0, alpha: 1.0)//蓝色slider.maximumTrackTintColor = UIColor.clear // 透明颜色slider.setThumbImage(UIImage(named: "slider_pointer_normal"), for: .normal)//clearn_point(透明) slider_pointer_normal(白色)slider.setThumbImage(UIImage(named: "slider_pointer_normal"), for: .highlighted)slider.setThumbImage(UIImage(named: "slider_pointer_normal"), for: .selected)let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(sliderTapped(gestureRecognizer:)))slider.addGestureRecognizer(tapGestureRecognizer)return slider}()//自定义point点lazy private var draggableButton: UIButton = {let button = UIButton()button.frame = CGRect(x: 0, y: 0, width: 10, height: 20)//button.backgroundColor = .blue.withAlphaComponent(0.5)button.setImage(UIImage(named: "slider_pointer_normal"), for: .normal)button.tag = 111return button}()// 修改播放进度var changePointBlock:((_ point: Float) ->Void)?// point数组var pointList = [Float]()// 当前播放点的移动位置var pointX:Float = 0.0 {didSet {let newX = (self.frame.width - draggableButton.frame.width) * CGFloat(pointX)if newX.isNaN { return }draggableButton.frame = CGRect(x: newX, y: draggableButton.frame.origin.y, width: draggableButton.frame.width, height: draggableButton.frame.height)slider.value = pointX}}override init(frame: CGRect) {super.init(frame: frame)buildUI()}required init?(coder aDecoder: NSCoder) {fatalError("init(coder:) has not been implemented")}private func buildUI() {backgroundColor = .clear // .green.withAlphaComponent(0.5)addSubview(progress)progress.snp.makeConstraints { make inmake.centerY.equalToSuperview()make.left.equalToSuperview().offset(2+1)//2是跟随slider的2+偏移1make.right.equalToSuperview().offset(-2)make.height.equalTo(4)}addSubview(slider)slider.snp.makeConstraints { make inmake.centerY.equalToSuperview().offset(-1)make.left.equalToSuperview().offset(2)make.right.equalToSuperview().offset(-2)make.height.equalTo(20)}addSubview(draggableButton)let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:)))draggableButton.addGestureRecognizer(panGesture)}override func layoutSubviews() {super.layoutSubviews()//严重的bug是否创建过if pointList.isEmpty { return }//如果有节点就移除for subview in self.subviews {if subview is UISlider {for sliderSubView in subview.subviews {if sliderSubView is UIButton {sliderSubView.removeFromSuperview()}}}}//布局节点for i in 0..<pointList.count {let button = UIButton()slider.addSubview(button)button.tag = ibutton.setImage(UIImage(named: "point_none"), for: .normal)button.addTarget(self, action: #selector(pointNoneClick), for: .touchUpInside)button.snp.makeConstraints { make inmake.width.equalTo(16)make.height.equalTo(20)make.centerY.equalToSuperview().offset(1)make.left.equalToSuperview().offset(Float(progress.bounds.size.width)*pointList[i]-16*0.5)}}}// MARK: - slider -- 点击滚动条@objc private func sliderTapped(gestureRecognizer: UIGestureRecognizer) {let pointTapped: CGPoint = gestureRecognizer.location(in: self)let positionOfSlider: CGPoint = slider.frame.originlet widthOfSlider: CGFloat = slider.frame.size.widthlet point = ((pointTapped.x - positionOfSlider.x) * CGFloat(slider.maximumValue) / widthOfSlider)//UI--修改进度条的值slider.setValue(Float(point), animated: false)//换算x的值let newX = (self.frame.width - draggableButton.frame.width) * CGFloat(point)//修改自定义point按键的framedraggableButton.frame = CGRect(x: newX, y: draggableButton.frame.origin.y, width: draggableButton.frame.width, height: draggableButton.frame.height)//闭包出去修改AVPlayer的值 0 -- 100changePointBlock?(Float(point))}// MARK: - 按键 -- 节点按键@objc private func pointNoneClick(button:UIButton) {//换算x的值let newX = (self.frame.width - draggableButton.frame.width) * CGFloat(pointList[button.tag])//修改自定义point按键的framedraggableButton.frame = CGRect(x: newX, y: draggableButton.frame.origin.y, width: draggableButton.frame.width, height: draggableButton.frame.height)//UI--修改进度条的值slider.setValue(Float(pointList[button.tag]), animated: false)//闭包出去修改AVPlayer的值 0 -- 100changePointBlock?(pointList[button.tag])}// MARK: - 自定义按键 -- 拖拽@objc func handlePanGesture(_ gesture: UIPanGestureRecognizer) {let translation = gesture.translation(in: self)//translation.x 拖拽的值var newX = draggableButton.frame.origin.x + translation.xswitch gesture.state {case .changed://更新按钮的 x 坐标,限制在 view 的范围内newX = max(0, min(newX, self.frame.width - draggableButton.frame.width))//意思是:newX取值范围0 ~ self.frame.width - draggableButton.frame.width 之间//修改自定义point按键的framedraggableButton.frame = CGRect(x: newX, y: draggableButton.frame.origin.y, width: draggableButton.frame.width, height: draggableButton.frame.height)gesture.setTranslation(.zero, in: self)//修改进度//换算成进度0.0-1.0let progress = newX / (self.frame.width - draggableButton.frame.width)//UI--修改进度条的值slider.setValue(Float(progress), animated: false)//闭包出去修改AVPlayer的值 0 -- 100changePointBlock?(Float(progress))default:break}}}

使用

创建+返回值出来
在这里插入图片描述
约束
在这里插入图片描述
赋值给它
在这里插入图片描述

相关文章:

Swift 技术 视频播放器滚动条(源码)

一直觉得自己写的不是技术&#xff0c;而是情怀&#xff0c;一个个的教程是自己这一路走来的痕迹。靠专业技能的成功是最具可复制性的&#xff0c;希望我的这条路能让你们少走弯路&#xff0c;希望我能帮你们抹去知识的蒙尘&#xff0c;希望我能帮你们理清知识的脉络&#xff0…...

PixelSNAIL论文代码学习(2)——门控残差网络的实现

文章目录 引言正文门控残差网络介绍门控残差网络具体实现代码使用pytorch实现 总结 引言 阅读了pixelSNAIL,很简短&#xff0c;就用了几页&#xff0c;介绍了网络结构&#xff0c;介绍了试验效果就没有了&#xff0c;具体论文学习链接 这段时间看他的代码&#xff0c;还是挺痛…...

WebGPU学习(9)---使用Pipeline Overridable Constants

使用Pipeline Overridable Constants WebGPU 的着色器语言是 WGSL&#xff0c;但与 GLSL 和 HLSL 不同&#xff0c;不支持 #ifdef 等宏。为了实现各种着色器变体&#xff0c;迄今为止&#xff0c;宏一直是着色器编程中非常重要的功能。那么应该如何处理没有宏的 WGSL&#xff…...

javaweb入门版学生信息管理系统-增删改查+JSP+Jstl+El

dao public class StudentDao {QueryRunner queryRunner QueryRunnerUtils.getQueryRunner();//查询全部学生信息public List<Student> selectStudent(){String sql "select * from tb_student";List<Student> students null;try {students queryRunn…...

云原生Kubernetes:K8S概述

目录 一、理论 1.云原生 2.K8S 3.k8s集群架构与组件 二、总结 一、理论 1.云原生 &#xff08;1&#xff09;概念 云原生是一种基于容器、微服务和自动化运维的软件开发和部署方法。它可以使应用程序更加高效、可靠和可扩展&#xff0c;适用于各种不同的云平台。 如果…...

nmap的使用

目录 nmap简介 主要作用 nmap原理 namp使用 options nmap列举远程机器开放端口 普通扫描 扫描范围端口 对几个端口探测 对所有端口进行探测 指定协议探测端口 扫描对应协议的所有端口 端口状态 nmap识别目标机器上服务的指纹 服务指纹 识别目标机器服务信息 …...

Python爬虫-某网酒店数据

前言 本文是该专栏的第5篇,后面会持续分享python爬虫案例干货,记得关注。 本文以某网的酒店数据为例,实现根据目标城市获取酒店数据。具体思路和方法跟着笔者直接往下看正文详细内容。(附带完整代码) 正文 地址:aHR0cHM6Ly93d3cuYnRoaG90ZWxzLmNvbS9saXN0L3NoYW5naGFp …...

了解atoi和offsetof

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 目录 文章目录 一、简介 二、深度剖析 1.atoi 2.offsetof 三、应用场景 一、简介二、深度剖析 1.atoi2.offsetof三、应用场景 一、简介 在C语言中&#xff0c;有许多…...

命令行编译VS工程

先输入以下命令&#xff0c;因为命令出错了&#xff0c;就会弹出帮助&#xff0c;如下&#xff1a; "C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\devenv.exe" /help 反正就是Microsoft Visual Studio 的安装路径。 帮助界面如下&#xff1a…...

Linux防火墙命令

开启防火墙 systemctl start firewalld关闭防火墙 systemctl stop firewalld # 暂时关闭防火墙 systemctl disable firewalld # 永久关闭防火墙(禁用开机自启) systemctl enable firewalld # 永久开启防火墙(启用开机自启)重启防火墙 systemctl restart firewalld重载规则 …...

大数据平台数据脱敏是什么意思?有哪些方案?

大数据平台包含了海量多样化数据&#xff0c;所以保障大数据平台数据安全非常重要&#xff0c;数据脱敏就是手段之一。今天我们就来简单聊聊大数据平台数据脱敏是什么意思&#xff1f;有哪些方案&#xff1f; 大数据平台数据脱敏是什么意思&#xff1f; 大数据平台数据脱敏简…...

前后端分离不存在会话,sessionid不一致问题

目录 1.使用拦截器解决跨域的示例&#xff1a; 2.使用redis&#xff0c;不使用session 前后端不分离项目我们可以通过session存储数据&#xff0c;但是前后端分离时不存在会话&#xff0c;每次请求sessionid都会改变&#xff0c;当值我们储存的数据不能取出来。 1.使用拦截器…...

Python 3+ 安装及pip配置

Python 3 安装及pip安装升级 1. 安装Python依赖2. 在Linux服务器下载3. 创建python链接4. 配置pip 服务器环境&#xff1a;Linux CentOS 7 内核版本3.10 Python版本&#xff1a;3.10.6 由于CentOS 7默认安装python2.7&#xff0c;使用yum可以查到最新的python3版本为3.6.8&…...

StarRocks入门到熟练

1、部署 1.1、注意事项 需要根据业务需求设计严谨的集群架构&#xff0c;一般来说&#xff0c;需要注意以下几项&#xff1a; 1.1.1、FE数量及高可用 FE的Follower要求为奇数个&#xff0c;且并不建议部署太多&#xff0c;通常我们推荐部署1个或3个Follower。在三个Followe…...

Zabbix Api监控项值推送:zabbix_sender

用法示例&#xff1a; zabbix_sender [-v] -z server [-p port] [-I IP-address] [-t timeout] -s host -k key -o value其中&#xff1a; -z 即 --zabbix-server&#xff0c;Zabbix server的主机名或IP地址。如果主机由proxy监控&#xff0c;则应使用proxy的主机名或IP地址-…...

Shell脚本开发:printf和test命令的实际应用

目录 Shell printf 命令 打印简单文本 Shell test 命令 1、文件测试 2、字符串比较 3、整数比较 逻辑运算&#xff1a; Shell printf 命令 当你使用Shell中的printf命令时&#xff0c;它可以帮助你格式化和输出文本。 打印简单文本 这将简单地打印字符串"Hello, …...

React笔记(三)类组件(1)

一、组件的概念 使用组件方式进行编程&#xff0c;可以提高开发效率&#xff0c;提高组件的复用性、提高代码的可维护性和可扩展性 React定义组件的方式有两种 类组件&#xff1a;React16.8版本之前几乎React使用都是类组件 函数组件:React16.8之后&#xff0c;函数式组件使…...

Hugging Face实战-系列教程4:padding与attention_mask

&#x1f6a9;&#x1f6a9;&#x1f6a9;Hugging Face 实战系列 总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在notebook中进行 本篇文章配套的代码资源已经上传 上篇内容&#xff1a; Hugging Face实战-系列教程3&#xff1a;文本2分类 下篇内容&#xff1a; …...

睿趣科技:抖音开网店卖玩具怎么样

近年来&#xff0c;随着社交媒体平台的飞速发展&#xff0c;抖音作为一款短视频分享应用也迅速崭露头角。而在这个充满创业机遇的时代背景下&#xff0c;许多人开始探索在抖音平台上开设网店&#xff0c;尤其是卖玩具类商品&#xff0c;那么抖音开网店卖玩具究竟怎么样呢? 首先…...

简易虚拟培训系统-UI控件的应用4

目录 Slider组件的常用参数 示例-使用Slider控制主轴 示例-Slider控制溜板箱的移动 本文以操作面板为例&#xff0c;介绍使用Slider控件控制开关和速度。 Slider组件的常用参数 Slider组件下面包含了3个子节点&#xff0c;都是Image组件&#xff0c;负责Slider的背景、填充区…...

在Ubuntu 22.04上搞定Gen6D位姿估计:从CUDA 11.8到Pytorch3D 0.7.8的完整环境搭建避坑指南

在Ubuntu 22.04上构建Gen6D位姿估计开发环境的全流程解析 计算机视觉领域的位姿估计技术正在重塑增强现实与机器人导航的边界。Gen6D作为香港大学团队开源的前沿项目&#xff0c;其无需CAD模型的特性为物体位姿识别提供了新思路。本文将彻底拆解Ubuntu 22.04环境下从驱动层到算…...

像素剧本圣殿新手指南:RPG对话框系统理解AI输出逻辑与修改技巧

像素剧本圣殿新手指南&#xff1a;RPG对话框系统理解AI输出逻辑与修改技巧 1. 认识像素剧本圣殿的RPG对话框系统 像素剧本圣殿的RPG对话框系统是其最具特色的交互界面&#xff0c;它模拟了经典像素游戏中NPC对话的场景。这个系统不仅仅是视觉上的复古设计&#xff0c;更是AI剧…...

C++的std--ranges中的验证编译期

C20引入的std::ranges库彻底改变了范围操作的方式&#xff0c;其中编译期验证机制是其最强大的特性之一。这种机制允许开发者在编译阶段捕获潜在错误&#xff0c;显著提升了代码的健壮性和性能。本文将深入探讨std::ranges中编译期验证的核心机制及其实际应用价值。编译时概念检…...

微信聊天记录的数字守护:WeChatMsg本地存储解决方案全解析

微信聊天记录的数字守护&#xff1a;WeChatMsg本地存储解决方案全解析 【免费下载链接】WeChatMsg 提取微信聊天记录&#xff0c;将其导出成HTML、Word、CSV文档永久保存&#xff0c;对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/W…...

Harness:统一企业级 DevOps 平台的新标准

核心导读&#xff1a;随着云计算和微服务架构的普及&#xff0c;传统 DevOps 工具链越来越碎片化。Harness 作为一个集 CI/CD、GitOps、功能发布、云成本管理、混沌工程于一身的企业级平台&#xff0c;正在改变团队的交付方式。本文深入探讨 Harness 如何解决现代化 DevOps 的核…...

SVM实战:从线性可分到核技巧的全面解析

1. SVM入门&#xff1a;从分类问题到最优超平面 第一次听说SVM时&#xff0c;我正被一个简单的二分类问题困扰着。手头有一组客户数据&#xff0c;需要根据消费习惯将他们分成两类。试过逻辑回归&#xff0c;效果勉强及格&#xff1b;用决策树又容易过拟合。直到同事推荐了SVM&…...

faster-whisper-GUI架构设计与性能优化:构建高效语音识别工作流的技术实践

faster-whisper-GUI架构设计与性能优化&#xff1a;构建高效语音识别工作流的技术实践 【免费下载链接】faster-whisper-GUI faster_whisper GUI with PySide6 项目地址: https://gitcode.com/gh_mirrors/fa/faster-whisper-GUI 在语音识别技术快速发展的今天&#xff0…...

springboot+vue基于web的社区交互图书管理系统的设计系统

目录同行可拿货,招校园代理 ,本人源头供货商系统功能模块划分关键技术实现数据库设计要点社区交互设计安全防护措施项目技术支持源码获取详细视频演示 &#xff1a;文章底部获取博主联系方式&#xff01;同行可合作同行可拿货,招校园代理 ,本人源头供货商 系统功能模块划分 后…...

开源工具技术解析与实践指南:突破游戏性能限制的完整方案

开源工具技术解析与实践指南&#xff1a;突破游戏性能限制的完整方案 【免费下载链接】genshin-fps-unlock unlocks the 60 fps cap 项目地址: https://gitcode.com/gh_mirrors/ge/genshin-fps-unlock 一、问题溯源&#xff1a;帧率限制背后的技术债务分析 当高端显卡在…...

Qwen3-ASR-1.7B实战教程:结合Punctuation Restoration模型提升标点准确率

Qwen3-ASR-1.7B实战教程&#xff1a;结合Punctuation Restoration模型提升标点准确率 语音识别技术已经相当成熟&#xff0c;但识别结果往往缺少标点符号&#xff0c;让长文本阅读变得困难。本文将教你如何将Qwen3-ASR-1.7B语音识别模型与标点恢复技术结合&#xff0c;获得既准…...