// // GestureSlider.swift // Crush // // Created by Leon on 2025/7/21. // import UIKit /// 音频专用滑动条 class CustomSlider: UIControl { private let trackView = UIView() private var tickViews: [UIView] = [] private let thumbView = UIView() var numberOfSteps: Int = 11 { didSet { setupTicks() layoutIfNeeded() } } @Published var selectedStep: Int = 5 { didSet { updateThumbPosition(animated: true) } } private let trackHeight: CGFloat = 20 private let tickWidth: CGFloat = 2 private let tickHeight: CGFloat = 12 private let thumbSize = CGSize(width: 8, height: 28) override init(frame: CGRect) { super.init(frame: frame) setup() } required init?(coder: NSCoder) { super.init(coder: coder) setup() } private func setup() { backgroundColor = .clear // Track trackView.backgroundColor = .c.csen trackView.layer.cornerRadius = 12 addSubview(trackView) trackView.snp.makeConstraints { make in make.edges.equalToSuperview() } // Thumb thumbView.backgroundColor = .c.cpvn thumbView.layer.cornerRadius = thumbSize.width / 2 addSubview(thumbView) // Gesture let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:))) addGestureRecognizer(pan) setupTicks() } private func setupTicks() { tickViews.forEach { $0.removeFromSuperview() } tickViews.removeAll() for _ in 0 ..< numberOfSteps { let tick = UIView() tick.backgroundColor = UIColor(white: 1, alpha: 0.3) addSubview(tick) tickViews.append(tick) } bringSubviewToFront(thumbView) } override func layoutSubviews() { super.layoutSubviews() // Layout ticks let lrPadding = 24.0 let ticksWitdh = bounds.width - lrPadding * 2 let spacing = ticksWitdh / CGFloat(numberOfSteps - 1) for (index, tick) in tickViews.enumerated() { let x = CGFloat(index) * spacing + lrPadding // 24 is leftPadding tick.frame = CGRect( x: x - tickWidth / 2, y: (bounds.height - tickHeight) / 2, width: tickWidth, height: tickHeight ) } updateThumbPosition(animated: false) } private func updateThumbPosition(animated: Bool) { guard numberOfSteps > 1 else { return } let lrPadding = 24.0 let ticksWitdh = bounds.width - lrPadding * 2 let spacing = ticksWitdh / CGFloat(numberOfSteps - 1) let x = CGFloat(selectedStep) * spacing - thumbSize.width / 2 + lrPadding let y = (bounds.height - thumbSize.height) / 2 let update = { self.thumbView.frame = CGRect(origin: CGPoint(x: x, y: y), size: self.thumbSize) } if animated { UIView.animate(withDuration: 0.2, animations: update) } else { update() } } @objc private func handlePan(_ gesture: UIPanGestureRecognizer) { let location = gesture.location(in: self) let stepWidth = bounds.width / CGFloat(numberOfSteps - 1) var newStep = Int(round(location.x / stepWidth)) newStep = max(0, min(numberOfSteps - 1, newStep)) if selectedStep != newStep { selectedStep = newStep sendActions(for: .valueChanged) } if gesture.state == .ended { updateThumbPosition(animated: true) } } }