// // BFChooseMusicView.swift // BFRecordScreenKit // // Created by 胡志强 on 2022/2/28. // import Foundation import UIKit import BFUIKit import BFMediaKit import BFCommonKit import BFNetRequestKit import Alamofire import MJRefresh enum BFChooseMusicViewClickType { case sure, cancle, search } struct MusicCategoryModel { var id : Int64 var name: String? var page = 1 mutating func changePage() { page += 1 } } class BFChooseMusicView: UIView { var clickBtnAction: ((BFChooseMusicViewClickType) -> Void)? var cutActionCallback: ((PQVoiceModel) -> Void)? // 各类别音乐列表,以类别id为key var musicDic = [Int64 : [PQVoiceModel]]() var playManager = BFMusicManager() var searchModel : PQVoiceModel?{ willSet{ if searchModel != nil { if let catogory = categories.first(where: { mod in mod.name == "热门" }){ if let _ = musicDic[catogory.id] { // 找到热门的数组 musicDic[catogory.id]?.removeFirst() } } } } didSet { if let searchModel = searchModel { if let catogory = categories.first(where: { mod in mod.name == "热门" }){ if let list = musicDic[catogory.id] { var arr = [PQVoiceModel]() arr.append(searchModel) if list.first?.musicId != searchModel.musicId { arr.append(contentsOf: list) musicDic[catogory.id] = arr searchModel.isSelected = true chosedMusic?.isSelected = false chosedMusic = searchModel musicTb.reloadData() } } } } } } // 选择的音乐类别 var categorySelectIndex : Int = 1 var currPage : Int64 = 1 // 音乐类别 var categories = [MusicCategoryModel]() var musicVolume = 0 { didSet{ slidV.value = Float(musicVolume) / 100.0 uiForChangeVolume() } } // 选中的音乐 var chosedMusic : PQVoiceModel? var chosedIndexPath : IndexPath? var chosedCellStatu : BFMuicInfoCellState = .pause lazy var categoryView : UICollectionView = { let layout = BFCollectionViewFlowLayout() layout.dele = self layout.scrollDirection = .horizontal layout.minimumLineSpacing = 25 // layout.sectionInset = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10) layout.minimumInteritemSpacing = 2 let vv = UICollectionView(frame: CGRect(x: 60, y: 0, width: cScreenWidth - 60, height: 40), collectionViewLayout: layout) vv.isPagingEnabled = false vv.showsHorizontalScrollIndicator = false vv.showsVerticalScrollIndicator = false vv.register(BFMusicCategoryCell.self, forCellWithReuseIdentifier: "BFMusicCategoryCell") vv.backgroundColor = UIColor.clear vv.delegate = self vv.dataSource = self if #available(iOS 11.0, *) { vv.contentInsetAdjustmentBehavior = .never } else { // Fallback on earlier versions } vv.backgroundColor = .clear return vv }() lazy var progressL : UILabel = { let l = UILabel() l.textColor = UIColor.hexColor(hexadecimal: "#9d9d9d") l.font = UIFont.systemFont(ofSize: 13, weight: .regular) l.textAlignment = .center l.text = "0%" return l }() lazy var slidV : UISlider = { let v = UISlider() v.addTarget(self, action: #selector(valuChange(slid:)), for: .valueChanged) return v }() lazy var musicTb : UITableView = { let tb = UITableView(frame: .zero) tb.delegate = self tb.dataSource = self tb.separatorColor = UIColor.hexColor(hexadecimal: "#272727") tb.register(BFMuicInfoCell.self, forCellReuseIdentifier: "BFMuicInfoCell") tb.backgroundColor = .clear // tb.mj_footer = MJRefreshAutoFooter.init(refreshingBlock: {[weak self] in // guard let wself = self else { return } // // let cate = wself.categories[wself.categorySelectIndex] // if (wself.musicDic[cate.id]?.count ?? 0) > 0 { // wself.getMusicsForCategory() // } // }) return tb }() override init(frame: CGRect) { super.init(frame: frame) backgroundColor = UIColor.clear let dismisBtn = UIButton() dismisBtn.tag = 1004 dismisBtn.frame = CGRect(x: 0, y: 0, width: width, height: 200) dismisBtn.addTarget(self, action: #selector(btnAction(btn:)), for: .touchUpInside) addSubview(dismisBtn) let backV = UIView() backV.backgroundColor = .black backV.isUserInteractionEnabled = true backV.layer.cornerRadius = 10 backV.frame = CGRect(x: 0, y: 200, width: cScreenWidth, height: cScreenHeigth - 190) addSubview(backV) let cancelBtn = UIButton() cancelBtn.tag = 1001 cancelBtn.frame = CGRect(x: 18, y: 227, width: 35, height: 24) cancelBtn.setTitle("取消", for: .normal) cancelBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#616161"), for: .normal) cancelBtn.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .regular) cancelBtn.addTarget(self, action: #selector(btnAction(btn:)), for: .touchUpInside) addSubview(cancelBtn) let sureBtn = UIButton() sureBtn.tag = 1002 sureBtn.frame = CGRect(x: width - 35 - 18, y: 227, width: 35, height: 24) sureBtn.setTitle("确定", for: .normal) sureBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#389AFF"), for: .normal) sureBtn.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .regular) sureBtn.addTarget(self, action: #selector(btnAction(btn:)), for: .touchUpInside) addSubview(sureBtn) let line1 = UIView() line1.backgroundColor = UIColor.hexColor(hexadecimal: "#272727") line1.frame = CGRect(x: 0, y: 260, width: width, height: 1) addSubview(line1) categoryView.frame = CGRect(x: 0, y: line1.bottomY, width: width - 50, height: 40) addSubview(categoryView) let searchBtn = UIButton() searchBtn.tag = 1003 searchBtn.frame = CGRect(x: categoryView.rightX+2, y: line1.bottomY, width: 40, height: 40) searchBtn.setImage(imageInRecordScreenKit(by: "search"), for: .normal) searchBtn.addTarget(self, action: #selector(btnAction(btn:)), for: .touchUpInside) addSubview(searchBtn) let line2 = UIView() line2.backgroundColor = UIColor.hexColor(hexadecimal: "#272727") line2.frame = CGRect(x: 0, y: categoryView.bottomY, width: width, height: 1) addSubview(line2) musicTb.frame = CGRect(x: 0, y: line2.bottomY, width: width, height: height - line2.bottomY - 100) addSubview(musicTb) let soundIV = UIImageView(image: imageInRecordScreenKit(by: "soundBtn")) soundIV.frame = CGRect(x: 16, y: musicTb.bottomY + 36, width: 28, height: 28) soundIV.contentMode = .scaleAspectFit addSubview(soundIV) slidV.frame = CGRect(x: soundIV.rightX + 20, y: soundIV.y+5, width: cScreenWidth - soundIV.rightX - 20 - 18, height: 18) addSubview(slidV) progressL.frame = CGRect(x: slidV.x - 7, y: slidV.y - 24, width: 36, height: 16) addSubview(progressL) self.musicVolume = 20 playManager.playStatusCallback = {[weak self] playerItem, status in guard let wself = self else { return } if let indx = wself.chosedIndexPath, let cell = wself.musicTb.cellForRow(at: indx) as? BFMuicInfoCell, (playerItem?.asset as? AVURLAsset)?.url.absoluteString ?? "b" == cell.data?.musicPath ?? "a" { DispatchQueue.main.async { cell.status = status } } wself.chosedCellStatu = status } prepareCatoryList() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } //MARK: - 请求数据 func prepareCatoryList() { let categoryListUrl = "produce-center/bgm/app/getAllBgmCates" BFNetRequestAdaptor.postRequestData(url: onlinePQTvApi + categoryListUrl, parames: [:], commonParams: commonParams(), encoding: JSONEncoding.default, isJsonEncodingNormal: false, timeoutInterval: 15) {[weak self] response, _, error, _ in guard let wself = self else { return } var tagsList = [MusicCategoryModel]() if response is NSNull || response == nil { cShowHUB(superView: nil, msg: "音乐分类获取失败") return } let tagsTempArr = response as? [[String: Any]] if tagsTempArr != nil, (tagsTempArr?.count ?? 0) > 0 { for dict in tagsTempArr! { let cid = (dict["cateId"] as? Int64 ?? 0) if cid > 0 { let tempMusic = MusicCategoryModel(id: cid, name: dict["cateName"] as? String) tagsList.append(tempMusic) } } } let tempMusic = MusicCategoryModel(id: -1, name: "") tagsList.insert(tempMusic, at: 0) wself.categories.append(contentsOf: tagsList) wself.categoryView.reloadData() if wself.categories.count > 1 { wself.categorySelectIndex = 1 wself.getMusicsForCategory() } } } func getMusicsForCategory() { let musicListUrl = "produce-center/bgm/pageCateBgm" let cate = categories[categorySelectIndex] BFNetRequestAdaptor.postRequestData(url: onlinePQTvApi + musicListUrl, parames: ["cateId": cate.id, "pageNum": cate.page, "pageSize": 100], commonParams: commonParams(), encoding: JSONEncoding.default) {[weak self] response, _, error, _ in guard let wself = self else { return } wself.musicTb.mj_footer?.endRefreshing() if response is NSNull || response == nil { cShowHUB(superView: nil, msg: "该分类下无音乐") return } if wself.musicDic[cate.id] == nil { wself.musicDic[cate.id] = [PQVoiceModel]() } var musicPageList = [PQVoiceModel]() let oldDataMusic : [PQVoiceModel] = wself.musicDic[cate.id]! if let dict = response as? [String: Any] { if let tempArr = dict["objs"] as? [[String: Any]] , tempArr.count > 0 { for (_, dic) in tempArr.enumerated() { let tempMusic = PQVoiceModel(jsonDict: dic) tempMusic.cacheTagID = cate.id if tempMusic.endTime <= tempMusic.startTime { tempMusic.endTime = tempMusic.startTime + 40 } let haveIndex = oldDataMusic.firstIndex(where: { (music) -> Bool in music.musicId == tempMusic.musicId }) if haveIndex == nil { musicPageList.append(tempMusic) } } } if musicPageList.count > 0 { if let ind = wself.categories.firstIndex(where: {$0.id == cate.id}) { wself.categories[ind].changePage() } wself.musicDic[cate.id]!.append(contentsOf: musicPageList) if cate.id == wself.categories[wself.categorySelectIndex].id{ wself.musicTb.reloadData() } } if (dict["totalSize"] as? Int64 ?? 0) <= (dict["offset"] as? Int64 ?? -1) { // 不再需要数据拉新 } } } } @objc func valuChange(slid:UISlider) { musicVolume = Int(slid.value * 100) } func uiForChangeVolume() { progressL.text = String(format: "%d%%", musicVolume) var frame = progressL.frame frame.origin.x = slidV.x - 7 + ((slidV.width - 28) / 100.0) * CGFloat(musicVolume) progressL.frame = frame playManager.player.volume = Float(musicVolume) / 100.0 chosedMusic?.volume = musicVolume } func configCategoriesView(){ categoryView.reloadData() } func choseCell() -> BFMuicInfoCell? { if let indpx = chosedIndexPath, let cell = musicTb.cellForRow(at: indpx) as? BFMuicInfoCell { return cell } return nil } @objc func btnAction(btn:UIButton) { playManager.pause() choseCell()?.status = .pause switch btn.tag{ case 1001: clickBtnAction?(.cancle) chosedMusic?.isSelected = false chosedMusic = nil choseCell()?.status = .normal chosedIndexPath = nil chosedCellStatu = .normal case 1002, 1004: clickBtnAction?(.sure) case 1003: clickBtnAction?(.search) default:break } } func reloadView() { } // 禁用音乐 func inhibitChooseMusic() { chosedMusic = nil playManager.pause(.normal) // choseCell()?.status = .normal choseCell()?.changeSelected(false) chosedIndexPath = nil } func refreshCurrSelectModel(mod:PQVoiceModel?) { chosedMusic = mod if let mod = mod { mod.isSelected = true } musicTb.reloadData() } } extension BFChooseMusicView:UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "BFMuicInfoCell", for: indexPath) as! BFMuicInfoCell cell.selectionStyle = .none cell.cutCallBack = {[weak self, weak cell] in guard let wself = self else { return } wself.playManager.pause() if let data = cell?.data { wself.cutActionCallback?(data) } } let tagid = categories[categorySelectIndex].id if let arr = musicDic[tagid] { cell.data = arr[indexPath.row] if cell.data?.isSelected ?? false { chosedIndexPath = indexPath chosedMusic = cell.data // cell.status = chosedCellStatu tableView.selectRow(at: indexPath, animated: false, scrollPosition: UITableView.ScrollPosition.none) cell.status = .pause playManager.replaceCurrentItem(musicPath: chosedMusic?.musicPath) }else{ cell.status = .normal } } return cell } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 64 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if categories.count > 1 { return musicDic[categories[categorySelectIndex].id]?.count ?? 0 } return 0 } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if let cell = tableView.cellForRow(at: indexPath) as? BFMuicInfoCell { if chosedIndexPath == indexPath { if cell.status != .pause{ cell.status = .pause playManager.pause() }else{ BFLog(1, message: "status: 2") cell.status = .loading playManager.play() } return } chosedMusic?.isSelected = false chosedMusic = cell.data chosedMusic?.volume = musicVolume cell.changeSelected(true) chosedIndexPath = indexPath if cell.status == .normal { if let urlStr = cell.data?.musicPath, let url = URL(string: urlStr){ BFLog(1, message: "歌曲地址: \(url)") if url.absoluteString != (playManager.player.currentItem?.asset as? AVURLAsset)?.url.absoluteString ?? "b" { cell.status = .loading playManager.replaceCurrentItem(musicPath: urlStr) }else { cell.status = .playing } playManager.play() } }else if cell.status == .pause{ BFLog(1, message: "status: 2") playManager.play() } else{ playManager.pause() cell.status = .pause } chosedCellStatu = cell.status } } func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) { let cell = tableView.cellForRow(at: indexPath) as? BFMuicInfoCell cell?.status = .normal cell?.changeSelected(false) chosedIndexPath = nil playManager.pause() } } extension BFChooseMusicView : UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{ func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return categories.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "BFMusicCategoryCell", for: indexPath) as! BFMusicCategoryCell cell.addData(dic: categories[indexPath.row]) cell.isChoosed = (indexPath.row == categorySelectIndex) return cell } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let oldCell = collectionView.cellForItem(at: IndexPath(row: categorySelectIndex, section: 0)) as? BFMusicCategoryCell let newCell = collectionView.cellForItem(at: indexPath) as? BFMusicCategoryCell if indexPath.row > 0 { categorySelectIndex = indexPath.row musicTb.reloadData() oldCell?.isChoosed = false newCell?.isChoosed = true getMusicsForCategory() }else { inhibitChooseMusic() } } } extension BFChooseMusicView: BFFlowLayoutDelegate{ func flowLayout(_ flowLayout: BFCollectionViewFlowLayout, itemHeight indexPath: IndexPath) -> CGFloat { var itemWidth = 30.0 if let title = categories[indexPath.row].name, title.count > 0{ let ww = title.textAutoWidth(height: 20, font: UIFont.systemFont(ofSize: 15, weight: .regular)) itemWidth = max(30.0, ww) } return itemWidth } } extension String{ func textAutoWidth(height:CGFloat, font:UIFont) ->CGFloat{ let string = self as NSString let origin = NSStringDrawingOptions.usesLineFragmentOrigin let lead = NSStringDrawingOptions.usesFontLeading let rect = string.boundingRect(with:CGSize(width:0, height: height), options: [origin,lead], attributes: [NSAttributedString.Key.font:font], context:nil) return rect.width } }