一直觉得自己写的不是技术而是情怀一个个的教程是自己这一路走来的痕迹。靠专业技能的成功是最具可复制性的希望我的这条路能让你们少走弯路希望我能帮你们抹去知识的蒙尘希望我能帮你们理清知识的脉络希望未来技术之巅上有你们也有我。文章目录前言思路重点使用Bug(重点)拖拽滚动条切换进度,滚动条回弹闪烁的问题.我把msu8开发好的Swift项目放在这里,后期写一个m3u8播放器文章在移动过去:Swift-(iOS 15 16)m3u8视频播放器整个的原理在视频中已经说的很清楚了,不懂的话直接看视频是很明朗的.自定义播放滚动条原理 视频解说下载链接自定义视频播放滑进度条示例代码前言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 UIKit class PlayProgressView:UIView{// 预加载进度lazy var progress:UIProgressView{let progressUIProgressView()progress.progressTintColorUIColor(red:169.0/255.0,green:169.0/255.0,blue:169.0/255.0,alpha:1.0)//深灰色progress.trackTintColorUIColor(red:228.0/255.0,green:228.0/255.0,blue:228.0/255.0,alpha:1.0)//浅灰色returnprogress}()// 进度条 clearn_point slider_pointer_normallazy var slider:UISlider{[weakself]inlet sliderUISlider()slider.minimumTrackTintColorUIColor(red:74.0/255.0,green:155.0/255.0,blue:234.0/255.0,alpha:1.0)//蓝色slider.maximumTrackTintColorUIColor.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 tapGestureRecognizerUITapGestureRecognizer(target:self,action:#selector(sliderTapped(gestureRecognizer:)))slider.addGestureRecognizer(tapGestureRecognizer)returnslider}()//自定义point点lazy private var draggableButton:UIButton{let buttonUIButton()button.frameCGRect(x:0,y:0,width:10,height:20)//button.backgroundColor .blue.withAlphaComponent(0.5)button.setImage(UIImage(named:slider_pointer_normal),for:.normal)button.tag111returnbutton}()// 修改播放进度var changePointBlock:((_ point:Float)-Void)?// point数组var pointList[Float]()// 当前播放点的移动位置var pointX:Float0.0{didSet{let newX(self.frame.width-draggableButton.frame.width)*CGFloat(pointX)ifnewX.isNaN{return}draggableButton.frameCGRect(x:newX,y:draggableButton.frame.origin.y,width:draggableButton.frame.width,height:draggableButton.frame.height)slider.valuepointX}}overrideinit(frame:CGRect){super.init(frame:frame)buildUI()}required init?(coder aDecoder:NSCoder){fatalError(init(coder:) has not been implemented)}private funcbuildUI(){backgroundColor.clear// .green.withAlphaComponent(0.5)addSubview(progress)progress.snp.makeConstraints{makeinmake.centerY.equalToSuperview()make.left.equalToSuperview().offset(21)//2是跟随slider的2偏移1make.right.equalToSuperview().offset(-2)make.height.equalTo(4)}addSubview(slider)slider.snp.makeConstraints{makeinmake.centerY.equalToSuperview().offset(-1)make.left.equalToSuperview().offset(2)make.right.equalToSuperview().offset(-2)make.height.equalTo(20)}addSubview(draggableButton)let panGestureUIPanGestureRecognizer(target:self,action:#selector(handlePanGesture(_:)))draggableButton.addGestureRecognizer(panGesture)}override funclayoutSubviews(){super.layoutSubviews()//严重的bug是否创建过ifpointList.isEmpty{return}//如果有节点就移除forsubviewinself.subviews{ifsubview is UISlider{forsliderSubViewinsubview.subviews{ifsliderSubView is UIButton{sliderSubView.removeFromSuperview()}}}}//布局节点foriin0..pointList.count{let buttonUIButton()slider.addSubview(button)button.tagi button.setImage(UIImage(named:point_none),for:.normal)button.addTarget(self,action:#selector(pointNoneClick),for:.touchUpInside)button.snp.makeConstraints{makeinmake.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 funcsliderTapped(gestureRecognizer:UIGestureRecognizer){let pointTapped:CGPointgestureRecognizer.location(in:self)let positionOfSlider:CGPointslider.frame.origin let widthOfSlider:CGFloatslider.frame.size.width let 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.frameCGRect(x:newX,y:draggableButton.frame.origin.y,width:draggableButton.frame.width,height:draggableButton.frame.height)//闭包出去修改AVPlayer的值 0 -- 100changePointBlock?(Float(point))}// MARK: - 按键 -- 节点按键objc private funcpointNoneClick(button:UIButton){//换算x的值let newX(self.frame.width-draggableButton.frame.width)*CGFloat(pointList[button.tag])//修改自定义point按键的framedraggableButton.frameCGRect(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 funchandlePanGesture(_ gesture:UIPanGestureRecognizer){let translationgesture.translation(in:self)//translation.x 拖拽的值var newXdraggableButton.frame.origin.xtranslation.xswitchgesture.state{case.changed://更新按钮的 x 坐标限制在 view 的范围内newXmax(0,min(newX,self.frame.width-draggableButton.frame.width))//意思是:newX取值范围0 ~ self.frame.width - draggableButton.frame.width 之间//修改自定义point按键的framedraggableButton.frameCGRect(x:newX,y:draggableButton.frame.origin.y,width:draggableButton.frame.width,height:draggableButton.frame.height)gesture.setTranslation(.zero,in:self)//修改进度//换算成进度0.0-1.0let progressnewX/(self.frame.width-draggableButton.frame.width)//UI--修改进度条的值slider.setValue(Float(progress),animated:false)//闭包出去修改AVPlayer的值 0 -- 100changePointBlock?(Float(progress))default:break}}}使用创建返回值出来约束赋值给它Bug(重点)拖拽滚动条切换进度,滚动条回弹闪烁的问题.该bug应该写在视频播放器里面,目前先总结到这里滑动闪烁的问题修好-滑动没有问题造成这个问题的原因是:当修改进度的时候(player.seek(to: seekTime)),并不是马上就能获取当前的进度就是修改好的进度,监听获取当前进度(player.addPeriodicTimeObserver(forInterval:),在没有修改进度成功之间返回的进度还是没有拖拽的进度.所以就会出现拖拽进度后,出现闪烁的问题(闪烁回去没有修改的进度点哪里). 修改方法1.新建一个属性,当拖拽之后,(player.addPeriodicTimeObserver(forInterval:)监听进度方法禁止更新.2.当拖拽进度之后,等到进度修改成功了,更新当前进度条,之后才去允许,(player.addPeriodicTimeObserver(forInterval:)监听进度方法更新.