一直觉得自己写的不是技术,而是情怀,一个个的教程是自己这一路走来的痕迹。靠专业技能的成功是最具可复制性的,希望我的这条路能让你们少走弯路,希望我能帮你们抹去知识的蒙尘,希望我能帮你们理清知识的脉络,希望未来技术之巅上有你们也有我。
整个的原理在视频中已经说的很清楚了,不懂的话直接看视频是很明朗的.
前言
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 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_normal
lazy var slider: UISlider = { [weak self] in
let 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 = 111
return 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 in
make.centerY.equalToSuperview()
make.left.equalToSuperview().offset(2+1)//2是跟随slider的2+偏移1
make.right.equalToSuperview().offset(-2)
make.height.equalTo(4)
}
addSubview(slider)
slider.snp.makeConstraints { make in
make.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 = i
button.setImage(UIImage(named: "point_none"), for: .normal)
button.addTarget(self, action: #selector(pointNoneClick), for: .touchUpInside)
button.snp.makeConstraints { make in
make.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.origin
let widthOfSlider: CGFloat = slider.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按键的frame
draggableButton.frame = CGRect(x: newX, y: draggableButton.frame.origin.y, width: draggableButton.frame.width, height: draggableButton.frame.height)
//闭包出去修改AVPlayer的值 0 -- 100
changePointBlock?(Float(point))
}
// MARK: - 按键 -- 节点按键
@objc private func pointNoneClick(button:UIButton) {
//换算x的值
let newX = (self.frame.width - draggableButton.frame.width) * CGFloat(pointList[button.tag])
//修改自定义point按键的frame
draggableButton.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 -- 100
changePointBlock?(pointList[button.tag])
}
// MARK: - 自定义按键 -- 拖拽
@objc func handlePanGesture(_ gesture: UIPanGestureRecognizer) {
let translation = gesture.translation(in: self)
//translation.x 拖拽的值
var newX = draggableButton.frame.origin.x + translation.x
switch 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按键的frame
draggableButton.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.0
let progress = newX / (self.frame.width - draggableButton.frame.width)
//UI--修改进度条的值
slider.setValue(Float(progress), animated: false)
//闭包出去修改AVPlayer的值 0 -- 100
changePointBlock?(Float(progress))
default:
break
}
}
}
使用
创建+返回值出来
约束
赋值给它