// // MPPositive_PlayerViewController.swift // MusicPlayer // // Created by Mr.Zhou on 2024/4/29. // import UIKit import Kingfisher ///b面播放器内容 class MPPositive_PlayerViewController: MPPositive_BaseViewController, UIViewControllerTransitioningDelegate { //播放器展示状态 enum PlayerShowType:Int { ///展示封面 case showCover = 0 ///展示歌词 case showLyric = 1 } //当前上方展示View展示内容(默认展示封面) private var topShowType:PlayerShowType = .showCover{ didSet{ setSwitchAnimation(topShowType) } } //MARK: - 导航View中的内容 //左侧向下的按钮 private lazy var leftPopBtn:UIButton = { let btn = UIButton() btn.backgroundColor = .init(hex: "#FFFFFF", alpha: 0.15) //添加白色边框 btn.layer.borderColor = UIColor(hex: "#FFFFFF", alpha: 0.3).cgColor btn.layer.borderWidth = 1*width btn.layer.cornerRadius = 21*width btn.layer.masksToBounds = true //设置图片 btn.setImage(UIImage(named: "Player_Pop'logo"), for: .normal) btn.addTarget(self, action: #selector(disMissClick(_ :)), for: .touchUpInside) return btn }() //中间单选按钮组View(SONG|LYRICS) private lazy var switchActionView:UIView = createSwitchActionView() //生成SONG按钮(默认选中) private lazy var songBtn:UIButton = createStatueBtn("SONG", color: .init(hex: "#FFFFFF", alpha: 0.85), tag: 0) //生成LYRICS按钮 private lazy var lyricsBtn:UIButton = createStatueBtn("LYRICS", color: .init(hex: "#FFFFFF", alpha: 0.45), tag: 1) //右侧关联按钮 private lazy var relatedBtn:UIButton = { let btn = UIButton() btn.setBackgroundImage(UIImage(named: "Player_Related'logo"), for: .normal) btn.addTarget(self, action: #selector(relatedContentClick(_ :)), for: .touchUpInside) return btn }() //MARK: - 背景视图 //背景封面图 private lazy var backImageView:UIImageView = { let imageView:UIImageView = .init() imageView.image = placeholderImage imageView.contentMode = .scaleAspectFill return imageView }() //背景模糊图层 private lazy var backBlurView:UIVisualEffectView = { // 创建一个模糊效果 let blurEffect = UIBlurEffect(style: .dark) // 创建一个可交互的毛玻璃视图 let blurEffectView = UIVisualEffectView(effect: blurEffect) blurEffectView.alpha = 1 blurEffectView.isUserInteractionEnabled = false return blurEffectView }() //MARK: - 底部按钮事件内容 //播放状态按钮 private lazy var playBtn:UIButton = { let btn:UIButton = .init() btn.setBackgroundImage(UIImage(named: "Player_Pause'logo"), for: .normal) btn.setBackgroundImage(UIImage(named: "Player_Player'logo"), for: .selected) btn.addTarget(self, action: #selector(playClick(_ :)), for: .touchUpInside) //默认无法交互(以免用户交互导致网络请求混乱) btn.isUserInteractionEnabled = true return btn }() //歌单列表按钮 private lazy var listBtn:UIButton = { let btn:UIButton = .init() btn.setBackgroundImage(UIImage(named: "Player_List'logo"), for: .normal) btn.addTarget(self, action: #selector(listClick(_ :)), for: .touchUpInside) return btn }() //播放类型按钮(按顺序播放,随机播放,单曲循环) private lazy var typeBtn:UIButton = { let btn = UIButton() btn.setBackgroundImage(UIImage(named: "List_NormolPlay'logo"), for: .normal) btn.addTarget(self, action: #selector(typeClick(_ :)), for: .touchUpInside) return btn }() //下一首按钮 private lazy var nextBtn:UIButton = { let btn = UIButton() btn.setBackgroundImage(UIImage(named: "Player_Next'logo"), for: .normal) btn.addTarget(self, action: #selector(nextClick(_ :)), for: .touchUpInside) return btn }() //上一首 private lazy var perviousBtn:UIButton = { let btn = UIButton() btn.setBackgroundImage(UIImage(named: "Player_Pervious'logo"), for: .normal) btn.addTarget(self, action: #selector(previousClick(_ :)), for: .touchUpInside) return btn }() //活动加载器 private lazy var activityIndicator:UIActivityIndicatorView = { let activityIndicator:UIActivityIndicatorView = .init(frame: .init(x: 0, y: 0, width: 66*width, height: 66*width)) activityIndicator.style = .gray activityIndicator.backgroundColor = .white activityIndicator.layer.masksToBounds = true activityIndicator.layer.cornerRadius = 33*width activityIndicator.color = .black return activityIndicator }() //封面View(封面,标题,副标题,收藏,下载,进度条View) private lazy var coverView:MPPositive_PlayerCoverView = .init(frame: .init(x: 0, y: 0, width: screen_Width, height: 480*width)) //歌词View private lazy var lyricsView:MPPositive_PlayerLyricView = .init(frame: .init(x: 0, y: 0, width: screen_Width, height: 480*width)) var recommendBlock:(() -> Void)? override func viewDidLoad() { super.viewDidLoad() //隐藏导航栏label setTitle("") configure() //打开时检索播放器状态,好调整内容 MP_PlayerManager.shared.runActionBlock = { [weak self] (currentTime, duration) in guard let self = self else { return } DispatchQueue.main.async { //展示当前时间 self.coverView.durationLabel.text = setTimesToMinSeconds(currentTime) //展示剩余时间 let remain:TimeInterval = duration - currentTime self.coverView.maxTimesLabel.text = setTimesToMinSeconds(remain) //调整进度条内容 let value = currentTime/duration self.coverView.sliderView.value = Float(value) } } //当缓存变化时 MP_PlayerManager.shared.cacheValueBlock = { [weak self] (value, duration) in guard let self = self else { return } DispatchQueue.main.async { if value < duration { //进度缓存中 let float = value/duration self.coverView.progressView.setProgress(Float(float), animated: false) }else { //进度缓存满了 self.coverView.progressView.setProgress(1, animated: false) } } } switchPlayBtnStatu(MP_PlayerManager.shared.getPlayState()) } deinit { NotificationCenter.default.removeObserver(self) } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) //添加监听 NotificationCenter.notificationKey.add(observer: self, selector: #selector(playerReloadAction(_ :)), notificationName: .positive_player_reload) NotificationCenter.notificationKey.add(observer: self, selector: #selector(statusSwitchAction(_:)), notificationName: .switch_player_status) NotificationCenter.notificationKey.add(observer: self, selector: #selector(playerTypeSwitchAction(_:)), notificationName: .player_type_switch) NotificationCenter.notificationKey.add(observer: self, selector: #selector(deleteListAction(_:)), notificationName: .player_delete_list) // switchPlayBtnStatu(MP_PlayerManager.shared.getPlayState()) //判断播放器是否装填数据 if MP_PlayerManager.shared.loadPlayer?.currentVideo != nil { uploadUI() } } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) NotificationCenter.default.removeObserver(self) } //视图配置 private func configure() { //导航View内容配置 navView.addSubview(leftPopBtn) leftPopBtn.snp.makeConstraints { make in make.width.height.equalTo(42*width) make.left.equalToSuperview().offset(16*width) make.centerY.equalToSuperview() } navView.addSubview(relatedBtn) relatedBtn.snp.makeConstraints { make in make.width.height.equalTo(24*width) make.right.equalToSuperview().offset(-16*width) make.centerY.equalToSuperview() } navView.addSubview(switchActionView) switchActionView.snp.makeConstraints { make in make.height.equalToSuperview() make.width.equalToSuperview().multipliedBy(0.45) make.center.equalToSuperview() } //背景内容配置 view.addSubview(backImageView) backImageView.snp.makeConstraints { make in make.left.right.top.bottom.equalToSuperview() } view.addSubview(backBlurView) backBlurView.snp.makeConstraints { make in make.left.right.top.bottom.equalToSuperview() } let bottomImageView:UIImageView = .init(image: .init(named: "Player_Bottom'mask")) bottomImageView.contentMode = .scaleAspectFill bottomImageView.isUserInteractionEnabled = false view.addSubview(bottomImageView) bottomImageView.snp.makeConstraints { make in make.left.right.bottom.equalToSuperview() make.height.equalTo(286*width) } //底部容器配置 let bottomView = createBottomView() view.addSubview(bottomView) bottomView.snp.makeConstraints { make in make.left.right.bottom.equalToSuperview() make.height.equalTo(200*width) } //配置封面和歌词 view.addSubview(lyricsView) lyricsView.snp.makeConstraints { make in make.top.equalTo(navView.snp.bottom) make.left.right.equalToSuperview() make.height.equalTo(480*width) } lyricsView.isHidden = true view.addSubview(coverView) coverView.snp.makeConstraints { make in make.top.equalTo(navView.snp.bottom) make.left.right.equalToSuperview() make.height.equalTo(480*width) } coverView.isHidden = false } //生成一个单选按钮组View private func createSwitchActionView() -> UIView { let actionView:UIView = UIView() actionView.backgroundColor = .clear actionView.addSubview(songBtn) songBtn.snp.makeConstraints { make in make.centerY.equalToSuperview() make.centerX.equalToSuperview().multipliedBy(0.5) } actionView.addSubview(lyricsBtn) lyricsBtn.snp.makeConstraints { make in make.centerY.equalToSuperview() make.centerX.equalToSuperview().multipliedBy(1.5) } //生成间隔线View let lineView:UIView = .init() lineView.backgroundColor = .init(hex: "#FFFFFF", alpha: 0.45) actionView.addSubview(lineView) lineView.snp.makeConstraints { make in make.center.equalToSuperview() make.width.equalTo(1*width) make.height.equalTo(14*width) } //生成按钮 return actionView } //生成页面状态按钮 private func createStatueBtn(_ text:String = "text", color:UIColor, tag:Int) -> UIButton { let btn = UIButton() btn.setTitle(text, for: .normal) btn.setTitleColor(color, for: .normal) btn.titleLabel?.font = .systemFont(ofSize: 15, weight: .regular) btn.tag = tag btn.addTarget(self, action: #selector(switchActionClick(_ :)), for: .touchUpInside) return btn } //生成一个底部容器View private func createBottomView() -> UIView { let bottomView:UIView = .init() bottomView.backgroundColor = .clear bottomView.addSubview(playBtn) playBtn.snp.makeConstraints { make in make.width.height.equalTo(66*width) make.centerX.equalToSuperview() make.centerY.equalToSuperview().multipliedBy(1.1) } bottomView.addSubview(activityIndicator) activityIndicator.snp.makeConstraints { make in make.center.equalTo(playBtn) make.width.height.equalTo(playBtn) } let state = MP_PlayerManager.shared.getPlayState() switch state { case .Null: activityIndicator.isHidden = false activityIndicator.startAnimating() default: activityIndicator.isHidden = false activityIndicator.stopAnimating() } bottomView.addSubview(listBtn) listBtn.snp.makeConstraints { make in make.right.equalToSuperview().offset(-20*width) make.centerY.equalTo(playBtn) make.width.height.equalTo(24*width) } bottomView.addSubview(typeBtn) typeBtn.snp.makeConstraints { make in make.left.equalToSuperview().offset(20*width) make.centerY.equalTo(playBtn) make.width.height.equalTo(24*width) } bottomView.addSubview(nextBtn) nextBtn.snp.makeConstraints { make in make.width.height.equalTo(30*width) make.centerY.equalTo(playBtn.snp.centerY) make.right.equalToSuperview().offset(-98*width) } bottomView.addSubview(perviousBtn) perviousBtn.snp.makeConstraints { make in make.width.height.equalTo(30*width) make.centerY.equalTo(playBtn.snp.centerY) make.left.equalToSuperview().offset(98*width) } return bottomView } //MARK: - 页面渲染 private func uploadUI() { DispatchQueue.main.async { [weak self] in guard let self = self, MP_PlayerManager.shared.loadPlayer?.currentVideo != nil else {return} print("\(MP_PlayerManager.shared.loadPlayer?.currentVideo?.title ?? "")刷新了页面") //填充数据 backImageView.kf.setImage(with: MP_PlayerManager.shared.loadPlayer.currentVideo?.coverUrl, placeholder: placeholderImage) coverView.coverImageView.kf.setImage(with: MP_PlayerManager.shared.loadPlayer.currentVideo?.coverUrl, placeholder: placeholderImage) coverView.titleLabel.text = MP_PlayerManager.shared.loadPlayer.currentVideo?.title coverView.subtitleLabel.text = MP_PlayerManager.shared.loadPlayer.currentVideo?.subtitle lyricsView.titleLabel.text = MP_PlayerManager.shared.loadPlayer.currentVideo?.title lyricsView.subtitleLabel.text = MP_PlayerManager.shared.loadPlayer.currentVideo?.subtitle lyricsView.lyricsLabel.text = (MP_PlayerManager.shared.loadPlayer.currentVideo?.lyrics ?? "").isEmpty == true ? "No Lyrics":MP_PlayerManager.shared.loadPlayer.currentVideo?.lyrics coverView.downloadButton.state = (MPPositive_DownloadItemModel.fetch(.init(format: "videoId == %@", MP_PlayerManager.shared.loadPlayer.currentVideo?.song.videoId ?? "")).count != 0) ? .downloaded:.startDownload coverView.downloadButton.isUserInteractionEnabled = !(MP_PlayerManager.shared.loadPlayer.currentVideo?.isDlownd ?? false) coverView.collectionSongBtn.isSelected = MP_PlayerManager.shared.loadPlayer.currentVideo?.isCollection ?? false coverView.restoreDownloadProgress() switchPlayTypeBtnIcon(typeBtn) // activityIndicator.isHidden = true // activityIndicator.stopAnimating() } } //MARK: - 通知 //播放器音乐刷新 @objc private func playerReloadAction(_ sender:Notification) { //渲染页面 DispatchQueue.main.async { [weak self] in guard let self = self else {return} uploadUI() //回正进度条 coverView.sliderView.value = 0 //调整时间值 coverView.durationLabel.text = setTimesToMinSeconds(0) //调整最大时间值 coverView.maxTimesLabel.text = setTimesToMinSeconds(0) //开始播放 MP_PlayerManager.shared.play { [weak self] in guard let self = self else { return } //允许playBtn按钮交互 playBtn.isUserInteractionEnabled = true } } } //切换播放器状态时 @objc private func statusSwitchAction(_ sender:Notification) { guard let object = sender.object else {return} DispatchQueue.main.async { [weak self] in guard let self = self else {return} let state:MP_PlayerStateType = sender.object as! MP_PlayerStateType DispatchQueue.main.async { [weak self] in self?.switchPlayBtnStatu(state) } } } private func switchPlayBtnStatu(_ state:MP_PlayerStateType) { switch state { case .Playing: playBtn.isSelected = true activityIndicator.isHidden = true activityIndicator.stopAnimating() case .Pause: playBtn.isSelected = false activityIndicator.isHidden = true activityIndicator.stopAnimating() default: playBtn.isSelected = false activityIndicator.isHidden = false activityIndicator.startAnimating() } } //切换播放器播放方式 @objc private func playerTypeSwitchAction(_ sender:Notification) { DispatchQueue.main.async { [weak self] in guard let self = self else {return} switchPlayTypeBtnIcon(typeBtn) } } //用户清空了歌单列表 @objc private func deleteListAction(_ sender:Notification) { DispatchQueue.main.async { [weak self] in guard let self = self else {return} dismiss(animated: true) } } //MARK: - 点击事件 //向下dismiss @objc private func disMissClick(_ sender:UIButton) { dismiss(animated: true) } //切换页面显示内容(单曲封面|歌词)按钮组 @objc private func switchActionClick(_ sender:UIButton) { guard MP_NetWorkManager.shared.netWorkStatu == .reachable else { MP_HUD.text("Weak connection.", delay: 2.0, completion: nil) return } topShowType = .init(rawValue: sender.tag)! } //切换动画 private func setSwitchAnimation(_ sender: PlayerShowType) { //切换时,禁止segmentedView交互 switchActionView.isUserInteractionEnabled = false switch sender { case .showCover://展示封面 UIView.animate(withDuration: 0.4) { [weak self] in guard let self = self else {return} //展示封面 coverView.alpha = 1.0 coverView.isHidden = false //lyricView隐藏 lyricsView.alpha = 0.1 songBtn.setTitleColor(.init(hex: "#FFFFFF", alpha: 0.85), for: .normal) lyricsBtn.setTitleColor(.init(hex: "#FFFFFF", alpha: 0.45), for: .normal) } completion: { [weak self] (_) in guard let self = self else {return} //平移结束后,隐藏lyricListView lyricsView.isHidden = true //完成状况允许交互 switchActionView.isUserInteractionEnabled = true } case .showLyric://展示歌词 UIView.animate(withDuration: 0.4) { [weak self] in guard let self = self else {return} //展示歌词 lyricsView.alpha = 1.0 lyricsView.isHidden = false //coverView向左平移一个屏幕尺度 coverView.alpha = 0.1 songBtn.setTitleColor(.init(hex: "#FFFFFF", alpha: 0.45), for: .normal) lyricsBtn.setTitleColor(.init(hex: "#FFFFFF", alpha: 0.85), for: .normal) } completion: { [weak self] (_) in guard let self = self else {return} //平移结束后,隐藏coverView coverView.isHidden = true //完成状况允许交互 switchActionView.isUserInteractionEnabled = true } } } //弹出关联内容 @objc private func relatedContentClick(_ sender:UIButton) { guard MP_NetWorkManager.shared.netWorkStatu == .reachable else { MP_HUD.text("Weak connection.", delay: 2.0, completion: nil) return } guard MP_PlayerManager.shared.loadPlayer?.currentVideo?.song.relatedID != nil else {return} //先dismiss if recommendBlock != nil { recommendBlock!() } dismiss(animated: true) } //播放/暂停/继续 @objc private func playClick(_ sender:UIButton) { // guard MP_NetWorkManager.shared.netWorkStatu == .reachable else { // MP_HUD.text("Weak connection.", delay: 2.0, completion: nil) // return // } guard MP_PlayerManager.shared.loadPlayer != nil else { return } //在当前音乐填充好之前,禁止触发点击 switch MP_PlayerManager.shared.getPlayState() { case .Null: //启动播放 MP_PlayerManager.shared.play { [weak self] in guard let self = self else { return } //回正进度条 coverView.sliderView.value = 0 } case .Playing: //播放中,进入暂停 MP_PlayerManager.shared.pause { [weak self] in } case .Pause: //暂停中,进入继续 MP_PlayerManager.shared.resume { [weak self] in } } } //展示列表 @objc private func listClick(_ sender:UIButton) { // guard MP_NetWorkManager.shared.netWorkStatu == .reachable else { // MP_HUD.text("Weak connection.", delay: 2.0, completion: nil) // return // } if MP_PlayerManager.shared.loadPlayer != nil { MPPositive_ModalType = .PlayerList let listVC = MPPositive_PlayerListShowViewController() listVC.transitioningDelegate = self listVC.modalPresentationStyle = .custom present(listVC, animated: true) } } func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { return MPPositive_PresentationController(presentedViewController: presented, presenting: presenting) } //切换播放器状态(按顺序/随机/单曲) @objc private func typeClick(_ sender:UIButton) { // guard MP_NetWorkManager.shared.netWorkStatu == .reachable else { // MP_HUD.text("Weak connection.", delay: 2.0, completion: nil) // return // } MPPositive_Debouncer.shared.call { [weak self] in guard let self = self else {return} //对播放器播放方式截形切换 var value = MP_PlayerManager.shared.getPlayType().rawValue value += 1 if value > 2 { value = 0 } MP_PlayerManager.shared.setPlayType(.init(rawValue: value)!) } } //下一首 @objc private func nextClick(_ sender:UIButton) { // guard MP_NetWorkManager.shared.netWorkStatu == .reachable else { // MP_HUD.text("Weak connection.", delay: 2.0, completion: nil) // return // } guard MP_PlayerManager.shared.loadPlayer?.currentVideo != nil else { return } //切歌广告 if MP_NetWorkManager.shared.netWorkStatu == .reachable { MP_AdMobManager.shared.showPlayInterstitialAdIfAvailable(.Switch, completion: nil) } MPPositive_Debouncer.shared.call { [weak self] in guard let self = self else {return} coverView.sliderView.value = 0 MP_PlayerManager.shared.nextEvent() } } //上一首 @objc private func previousClick(_ sender:UIButton) { // guard MP_NetWorkManager.shared.netWorkStatu == .reachable else { // MP_HUD.text("Weak connection.", delay: 2.0, completion: nil) // return // } guard MP_PlayerManager.shared.loadPlayer?.currentVideo != nil else { return } //切歌广告 if MP_NetWorkManager.shared.netWorkStatu == .reachable { MP_AdMobManager.shared.showPlayInterstitialAdIfAvailable(.Switch, completion: nil) } MPPositive_Debouncer.shared.call { [weak self] in guard let self = self else {return} coverView.sliderView.value = 0 MP_PlayerManager.shared.previousEvent() } } }