// // SPPlayerControlView.swift // Thimra // // Created by 曾觉新 on 2025/4/9. // import UIKit class SPPlayerControlView: UIView { weak var viewModel: SPPlayerListViewModel? { didSet { viewModel?.addObserver(self, forKeyPath: "isPlaying", context: nil) } } var shortModel: SPShortModel? { didSet { updateCollectButtonState() } } var videoInfo: SPVideoInfoModel? { didSet { } } ///滑动进度条 var panProgressFinishBlock: ((_ progress: CGFloat) -> Void)? ///0-1 var progress: CGFloat = 0 { didSet { progressView.progress = progress } } var durationTime: Int = 0 var currentTime: Int = 0 var isCurrent: Bool = false { didSet { updatePlayIconState() } } //MARK: UI属性 ///底部渐变层 private lazy var bottomGradientView: SPGradientView = { let view = SPGradientView() view.colors = [UIColor.color000000(alpha: 0).cgColor, UIColor.color000000(alpha: 0.5).cgColor] view.startPoint = .init(x: 0.5, y: 0) view.endPoint = .init(x: 0.5, y: 1) view.locations = [0, 1] view.isUserInteractionEnabled = false return view }() private(set) lazy var progressView: SPPlayerProgressView = { let view = SPPlayerProgressView() view.panStart = { [weak self] in guard let self = self else { return } self.panProgressStart() } view.panChange = { [weak self] progress in guard let self = self else { return } self.panProgressChange(progress: progress) } view.panFinish = { [weak self] progress in guard let self = self else { return } self.panProgressFinish(progress: progress) } return view }() private(set) lazy var toolView: UIView = { let view = UIView() view.backgroundColor = .color1C1C1E() return view }() private(set) lazy var playImageView: UIButton = { let button = UIButton(type: .custom) button.isHidden = true button.setImage(UIImage(named: "play_icon_01"), for: .normal) button.isUserInteractionEnabled = false return button }() ///右边功能区 private(set) lazy var rightFeatureView: UIStackView = { let view = UIStackView(arrangedSubviews: [collectButton]) view.axis = .vertical view.spacing = 25 return view }() ///收藏按钮 private lazy var collectButton: UIButton = { let button = createFeatureButton(title: "Save".localized, selectedTitle: "Added".localized, image: UIImage(named: "collect_icon_01"), selectedImage: UIImage(named: "collect_icon_01_selected")) button.addTarget(self, action: #selector(handleCollectButton), for: .touchUpInside) return button }() deinit { viewModel?.removeObserver(self, forKeyPath: "isPlaying") NotificationCenter.default.removeObserver(self) } override init(frame: CGRect) { super.init(frame: frame) NotificationCenter.default.addObserver(self, selector: #selector(updateShortCollectStateNotification), name: SPVideoAPI.updateShortCollectStateNotification, object: nil) let tap = UITapGestureRecognizer(target: self, action: #selector(handleScreen)) tap.delegate = self self.addGestureRecognizer(tap) sp_setupUI() } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "isPlaying" { updatePlayIconState() } } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func createFeatureButton(title: String?, selectedTitle: String? = nil, image: UIImage?, selectedImage: UIImage? = nil) -> UIButton { let button = JXButton(type: .custom) button.titleDirection = .down button.setImage(image, for: .normal) button.setImage(selectedImage, for: .selected) button.setImage(selectedImage, for: [.selected, .highlighted]) button.setTitle(title, for: .normal); button.setTitle(selectedTitle, for: .selected); button.setTitle(selectedTitle, for: [.selected, .highlighted]) button.setTitleColor(.colorFFFFFF(), for: .normal) // button.setTitleColor(.colorF564B6(), for: .selected) button.jx_font = .fontMedium(ofSize: 12) return button } @objc func handleScreen() { self.hadlePlayAndOrPaused() } func updatePlayIconState() { let isPlaying = self.viewModel?.isPlaying ?? false if isCurrent { playImageView.isHidden = isPlaying } else { playImageView.isHidden = true } } } extension SPPlayerControlView { private func sp_setupUI() { addSubview(bottomGradientView) addSubview(progressView) progressView.addSubview(toolView) addSubview(playImageView) addSubview(rightFeatureView) bottomGradientView.snp.makeConstraints { make in make.left.right.bottom.equalToSuperview() make.height.equalTo(kSPTabbarSafeBottomMargin + 200) } progressView.snp.makeConstraints { make in make.left.right.bottom.equalToSuperview() make.height.equalTo(40) } toolView.snp.makeConstraints { make in make.left.right.top.equalToSuperview() make.bottom.equalToSuperview().offset(-2) } playImageView.snp.makeConstraints { make in make.center.equalToSuperview() } rightFeatureView.snp.makeConstraints { make in make.right.equalToSuperview().offset(-15) make.bottom.equalToSuperview().offset(-200) } } } extension SPPlayerControlView { @objc private func hadlePlayAndOrPaused() { self.viewModel?.handlePauseOrPlay?() // guard let model = model as? SPShortModel else { return } // let vc = SPTVPlayerListViewController() // vc.shortPlayId = model.short_play_id // vc.videoId = model.video_info?.short_play_video_id // SPAPPTool.topViewController()?.navigationController?.pushViewController(vc, animated: true) } @objc private func handleCollectButton() { guard let shortPlayId = self.videoInfo?.short_play_id else { return } guard let videoId = self.videoInfo?.short_play_video_id else { return } let isCollect = !(self.shortModel?.is_collect ?? false) SPVideoAPI.requestCollectShort(isCollect: isCollect, shortPlayId: shortPlayId, videoId: videoId) { } } @objc private func updateShortCollectStateNotification(sender: Notification) { guard let userInfo = sender.userInfo else { return } guard let shortPlayId = userInfo["id"] as? String else { return } guard let isCollect = userInfo["state"] as? Bool else { return } guard shortPlayId == self.videoInfo?.short_play_id else { return } self.shortModel?.is_collect = isCollect; updateCollectButtonState() } private func updateCollectButtonState() { self.collectButton.isSelected = self.shortModel?.is_collect ?? false } } extension SPPlayerControlView { ///滑动进度开始 private func panProgressStart() { // self.isHidden = true // screenProgressView.duration = self.player?.duration ?? 0 // screenProgressView.frame = self.window?.bounds ?? .zero // // self.window?.addSubview(screenProgressView) // // var point = self.progressView.convert(CGPoint(x: 0, y: 0), to: self.window) // point.y = point.y + self.progressView.size.height - self.progressView.lineWidth / 2 // // screenProgressView.point = point } ///滑动进度中 private func panProgressChange(progress: CGFloat) { // screenProgressView.progress = progress } ///滑动进度结束 private func panProgressFinish(progress: CGFloat) { // self.isHidden = false // screenProgressView.removeFromSuperview() self.panProgressFinishBlock?(progress) } } //MARK: -------------- UIGestureRecognizerDelegate -------------- extension SPPlayerControlView: UIGestureRecognizerDelegate { func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { if touch.view != self { return false } else { return true } } }