// // SPPlayerDetailControlView.swift // Thimra // // Created by 曾觉新 on 2025/4/16. // import UIKit class SPPlayerDetailControlView: SPPlayerControlView { ///暂停按钮状态 enum PauseState { ///隐藏状态 case hidden ///显示暂停按钮 case showPause ///显示播放按钮 case showPlay } override var durationTime: Int { didSet { updateProgressTimeLabel() } } override var currentTime: Int { didSet { updateProgressTimeLabel() } } override var viewModel: SPPlayerListViewModel? { didSet { self.viewModel?.addObserver(self, forKeyPath: "speedModel", context: nil) updateSpeedButton() } } override var isCurrent: Bool { didSet { if isCurrent { showSpeedSelectedView(isShow: false) } else { self.timer?.invalidate() self.timer = nil self.pauseState = .hidden } } } ///暂停按钮状态 private lazy var pauseState = PauseState.hidden { didSet { switch pauseState { case .hidden: self.playImageView.isHidden = true self.retreatButton.isHidden = true self.advanceButton.isHidden = true case .showPause: self.playImageView.isHidden = false self.retreatButton.isHidden = false self.advanceButton.isHidden = false self.playImageView.setImage(UIImage(named: "pause_icon_01"), for: .normal) case .showPlay: self.playImageView.isHidden = false self.retreatButton.isHidden = false self.advanceButton.isHidden = false self.playImageView.setImage(UIImage(named: "play_icon_01"), for: .normal) } } } ///暂停按钮倒计时 private var timer: Timer? //MARK: UI属性 private lazy var episodeButton: UIButton = { let button = createFeatureButton(title: "Episodes".localized, image: UIImage(named: "episode_icon_02")) button.addTarget(self, action: #selector(handleEpisodeButton), for: .touchUpInside) return button }() private lazy var progressTimeLabel: UILabel = { let label = UILabel() label.font = .fontRegular(ofSize: 12) label.textColor = .colorFFFFFF() return label }() private lazy var speedButton: UIButton = { let button = UIButton(type: .custom) button.setBackgroundImage(UIImage(color: .colorFFFFFF(alpha: 0.2)), for: .normal) button.setTitleColor(.colorFFFFFF(alpha: 0.9), for: .normal) button.titleLabel?.font = .fontLight(ofSize: 11) button.layer.cornerRadius = 10 button.layer.masksToBounds = true button.addTarget(self, action: #selector(handleSpeedButton), for: .touchUpInside) return button }() private lazy var speedSelectedView: SPSpeedSelectedView = { let view = SPSpeedSelectedView() view.isHidden = true view.didSelectedSpeed = { [weak self] speedModel in guard let self = self else { return } self.viewModel?.setSpeedPlay(speedModel: speedModel) self.showSpeedSelectedView(isShow: false) } return view }() ///快退 private lazy var retreatButton: UIButton = { let button = UIButton(type: .custom) button.isHidden = true button.setImage(UIImage(named: "speed_icon_01"), for: .normal) button.addTarget(self, action: #selector(handleRetreatButton), for: .touchUpInside) return button }() ///快进 private lazy var advanceButton: UIButton = { let button = UIButton(type: .custom) button.isHidden = true button.setImage(UIImage(named: "speed_icon_02"), for: .normal) button.addTarget(self, action: #selector(handleAdvanceButton), for: .touchUpInside) return button }() deinit { self.viewModel?.removeObserver(self, forKeyPath: "speedModel") } override init(frame: CGRect) { super.init(frame: frame) self.playImageView.isUserInteractionEnabled = true self.playImageView.addTarget(self, action: #selector(handlePlayImageView), for: .touchUpInside) _setupUI() } @MainActor required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) if keyPath == "speedModel" { updateSpeedButton() } } private func updateProgressTimeLabel() { let currentTime = self.currentTime.formatTimeGroup() let durationTime = self.durationTime.formatTimeGroup() progressTimeLabel.text = "\(currentTime.1):\(currentTime.2)/\(durationTime.1):\(durationTime.2)" } ///更新速率按钮 private func updateSpeedButton() { self.speedButton.setTitle(self.viewModel?.speedModel.formatString(), for: .normal) } ///点击屏幕 override func handleScreen() { if self.pauseState == .hidden { self.pauseState = .showPause resetTimer() } else if self.pauseState == .showPause { self.pauseState = .hidden self.cleanTimer() } } override func updatePlayIconState() { guard isCurrent else { return } guard self.pauseState != .hidden else { return } if self.viewModel?.isPlaying == true { self.pauseState = .showPause self.resetTimer() } else { self.pauseState = .showPlay self.cleanTimer() } } @objc private func handleHiddenTimer() { self.pauseState = .hidden } ///清空计时器 private func cleanTimer() { self.timer?.invalidate() self.timer = nil } ///重置计时器 private func resetTimer() { cleanTimer() self.timer = Timer.scheduledTimer(timeInterval: 5, target: YYWeakProxy(target: self), selector: #selector(handleHiddenTimer), userInfo: nil, repeats: false) } } extension SPPlayerDetailControlView { private func _setupUI() { self.rightFeatureView.addArrangedSubview(episodeButton) toolView.addSubview(progressTimeLabel) addSubview(retreatButton) addSubview(advanceButton) // addSubview(speedButton) // addSubview(speedSelectedView) // self.progressView.snp.remakeConstraints { make in // make.left.equalToSuperview().offset(15) // make.centerX.equalToSuperview() // make.height.equalTo(30) // make.bottom.equalToSuperview().offset(-(kSPTabbarSafeBottomMargin + 10)) // } self.progressTimeLabel.snp.makeConstraints { make in // make.left.equalTo(self.progressView) // make.bottom.equalTo(self.progressView).offset(-12) make.centerY.equalToSuperview() make.left.equalToSuperview().offset(16) } retreatButton.snp.makeConstraints { make in make.centerY.equalTo(playImageView) make.right.equalTo(playImageView.snp.left).offset(kSPMainW(-50)) } advanceButton.snp.makeConstraints { make in make.centerY.equalTo(playImageView) make.left.equalTo(playImageView.snp.right).offset(kSPMainW(50)) } // speedButton.snp.makeConstraints { make in // make.centerY.equalTo(self.progressTimeLabel) // make.right.equalToSuperview().offset(-15) // make.width.equalTo(40) // make.height.equalTo(20) // } // speedSelectedView.snp.makeConstraints { make in // make.left.right.equalToSuperview() // make.bottom.equalTo(self.speedButton.snp.top).offset(-30) // } } } extension SPPlayerDetailControlView { @objc private func handleEpisodeButton() { self.viewModel?.handleEpisode?() } @objc private func handleSpeedButton() { showSpeedSelectedView(isShow: speedSelectedView.isHidden) } ///暂时或隐藏速率选择页面 private func showSpeedSelectedView(isShow: Bool) { speedSelectedView.isHidden = !isShow rightFeatureView.isHidden = isShow if isShow { self.speedSelectedView.currentSpeed = self.viewModel?.speedModel.speed ?? .x1 } } @objc private func handlePlayImageView() { self.viewModel?.handlePauseOrPlay?() } ///快退 @objc private func handleRetreatButton() { self.viewModel?.seekToTime(toTime: self.currentTime - 5) resetTimer() } ///快进 @objc private func handleAdvanceButton() { self.viewModel?.seekToTime(toTime: self.currentTime + 5) resetTimer() } }