BFChooseMusicView.swift 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. //
  2. // BFChooseMusicView.swift
  3. // BFRecordScreenKit
  4. //
  5. // Created by 胡志强 on 2022/2/28.
  6. //
  7. import Foundation
  8. import UIKit
  9. import BFUIKit
  10. import BFMediaKit
  11. import BFCommonKit
  12. import BFNetRequestKit
  13. import Alamofire
  14. import MJRefresh
  15. enum BFChooseMusicViewClickType {
  16. case sure, cancle, search
  17. }
  18. struct MusicCategoryModel {
  19. var id : Int64
  20. var name: String?
  21. var page = 1
  22. mutating func changePage() {
  23. page += 1
  24. }
  25. }
  26. class BFChooseMusicView: UIView {
  27. var clickBtnAction: ((BFChooseMusicViewClickType) -> Void)?
  28. var cutActionCallback: ((PQVoiceModel) -> Void)?
  29. // 各类别音乐列表,以类别id为key
  30. var musicDic = [Int64 : [PQVoiceModel]]()
  31. var playManager = BFMusicManager()
  32. var searchModel : PQVoiceModel?{
  33. willSet{
  34. if searchModel != nil {
  35. if let catogory = categories.first(where: { mod in
  36. mod.name == "热门"
  37. }){
  38. if let _ = musicDic[catogory.id] {
  39. // 找到热门的数组
  40. musicDic[catogory.id]?.removeFirst()
  41. }
  42. }
  43. }
  44. }
  45. didSet {
  46. if let searchModel = searchModel {
  47. if let catogory = categories.first(where: { mod in
  48. mod.name == "热门"
  49. }){
  50. if let list = musicDic[catogory.id] {
  51. var arr = [PQVoiceModel]()
  52. arr.append(searchModel)
  53. if list.first?.musicId != searchModel.musicId {
  54. arr.append(contentsOf: list)
  55. musicDic[catogory.id] = arr
  56. searchModel.isSelected = true
  57. chosedMusic?.isSelected = false
  58. chosedMusic = searchModel
  59. musicTb.reloadData()
  60. }
  61. }
  62. }
  63. }
  64. }
  65. }
  66. // 选择的音乐类别
  67. var categorySelectIndex : Int = 1
  68. var currPage : Int64 = 1
  69. // 音乐类别
  70. var categories = [MusicCategoryModel]()
  71. var musicVolume = 0 {
  72. didSet{
  73. slidV.value = Float(musicVolume) / 100.0
  74. uiForChangeVolume()
  75. }
  76. }
  77. // 选中的音乐
  78. var chosedMusic : PQVoiceModel?
  79. var chosedIndexPath : IndexPath?
  80. var chosedCellStatu : BFMuicInfoCellState = .pause
  81. lazy var categoryView : UICollectionView = {
  82. let layout = BFCollectionViewFlowLayout()
  83. layout.dele = self
  84. layout.scrollDirection = .horizontal
  85. layout.minimumLineSpacing = 25
  86. // layout.sectionInset = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)
  87. layout.minimumInteritemSpacing = 2
  88. let vv = UICollectionView(frame: CGRect(x: 60, y: 0, width: cScreenWidth - 60, height: 40), collectionViewLayout: layout)
  89. vv.isPagingEnabled = false
  90. vv.showsHorizontalScrollIndicator = false
  91. vv.showsVerticalScrollIndicator = false
  92. vv.register(BFMusicCategoryCell.self, forCellWithReuseIdentifier: "BFMusicCategoryCell")
  93. vv.backgroundColor = UIColor.clear
  94. vv.delegate = self
  95. vv.dataSource = self
  96. if #available(iOS 11.0, *) {
  97. vv.contentInsetAdjustmentBehavior = .never
  98. } else {
  99. // Fallback on earlier versions
  100. }
  101. vv.backgroundColor = .clear
  102. return vv
  103. }()
  104. lazy var progressL : UILabel = {
  105. let l = UILabel()
  106. l.textColor = UIColor.hexColor(hexadecimal: "#9d9d9d")
  107. l.font = UIFont.systemFont(ofSize: 13, weight: .regular)
  108. l.textAlignment = .center
  109. l.text = "0%"
  110. return l
  111. }()
  112. lazy var slidV : UISlider = {
  113. let v = UISlider()
  114. v.addTarget(self, action: #selector(valuChange(slid:)), for: .valueChanged)
  115. return v
  116. }()
  117. lazy var musicTb : UITableView = {
  118. let tb = UITableView(frame: .zero)
  119. tb.delegate = self
  120. tb.dataSource = self
  121. tb.separatorColor = UIColor.hexColor(hexadecimal: "#272727")
  122. tb.register(BFMuicInfoCell.self, forCellReuseIdentifier: "BFMuicInfoCell")
  123. tb.backgroundColor = .clear
  124. // tb.mj_footer = MJRefreshAutoFooter.init(refreshingBlock: {[weak self] in
  125. // guard let wself = self else { return }
  126. //
  127. // let cate = wself.categories[wself.categorySelectIndex]
  128. // if (wself.musicDic[cate.id]?.count ?? 0) > 0 {
  129. // wself.getMusicsForCategory()
  130. // }
  131. // })
  132. return tb
  133. }()
  134. override init(frame: CGRect) {
  135. super.init(frame: frame)
  136. backgroundColor = UIColor.clear
  137. let dismisBtn = UIButton()
  138. dismisBtn.tag = 1004
  139. dismisBtn.frame = CGRect(x: 0, y: 0, width: width, height: 200)
  140. dismisBtn.addTarget(self, action: #selector(btnAction(btn:)), for: .touchUpInside)
  141. addSubview(dismisBtn)
  142. let backV = UIView()
  143. backV.backgroundColor = .black
  144. backV.isUserInteractionEnabled = true
  145. backV.layer.cornerRadius = 10
  146. backV.frame = CGRect(x: 0, y: 200, width: cScreenWidth, height: cScreenHeigth - 190)
  147. addSubview(backV)
  148. let cancelBtn = UIButton()
  149. cancelBtn.tag = 1001
  150. cancelBtn.frame = CGRect(x: 18, y: 227, width: 35, height: 24)
  151. cancelBtn.setTitle("取消", for: .normal)
  152. cancelBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#616161"), for: .normal)
  153. cancelBtn.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .regular)
  154. cancelBtn.addTarget(self, action: #selector(btnAction(btn:)), for: .touchUpInside)
  155. addSubview(cancelBtn)
  156. let sureBtn = UIButton()
  157. sureBtn.tag = 1002
  158. sureBtn.frame = CGRect(x: width - 35 - 18, y: 227, width: 35, height: 24)
  159. sureBtn.setTitle("确定", for: .normal)
  160. sureBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#389AFF"), for: .normal)
  161. sureBtn.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .regular)
  162. sureBtn.addTarget(self, action: #selector(btnAction(btn:)), for: .touchUpInside)
  163. addSubview(sureBtn)
  164. let line1 = UIView()
  165. line1.backgroundColor = UIColor.hexColor(hexadecimal: "#272727")
  166. line1.frame = CGRect(x: 0, y: 260, width: width, height: 1)
  167. addSubview(line1)
  168. categoryView.frame = CGRect(x: 0, y: line1.bottomY, width: width - 50, height: 40)
  169. addSubview(categoryView)
  170. let searchBtn = UIButton()
  171. searchBtn.tag = 1003
  172. searchBtn.frame = CGRect(x: categoryView.rightX+2, y: line1.bottomY, width: 40, height: 40)
  173. searchBtn.setImage(imageInRecordScreenKit(by: "search"), for: .normal)
  174. searchBtn.addTarget(self, action: #selector(btnAction(btn:)), for: .touchUpInside)
  175. addSubview(searchBtn)
  176. let line2 = UIView()
  177. line2.backgroundColor = UIColor.hexColor(hexadecimal: "#272727")
  178. line2.frame = CGRect(x: 0, y: categoryView.bottomY, width: width, height: 1)
  179. addSubview(line2)
  180. musicTb.frame = CGRect(x: 0, y: line2.bottomY, width: width, height: height - line2.bottomY - 100)
  181. addSubview(musicTb)
  182. let soundIV = UIImageView(image: imageInRecordScreenKit(by: "soundBtn"))
  183. soundIV.frame = CGRect(x: 16, y: musicTb.bottomY + 36, width: 28, height: 28)
  184. soundIV.contentMode = .scaleAspectFit
  185. addSubview(soundIV)
  186. slidV.frame = CGRect(x: soundIV.rightX + 20, y: soundIV.y+5, width: cScreenWidth - soundIV.rightX - 20 - 18, height: 18)
  187. addSubview(slidV)
  188. progressL.frame = CGRect(x: slidV.x - 7, y: slidV.y - 24, width: 36, height: 16)
  189. addSubview(progressL)
  190. self.musicVolume = 20
  191. playManager.playStatusCallback = {[weak self] playerItem, status in
  192. guard let wself = self else { return }
  193. 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" {
  194. DispatchQueue.main.async {
  195. cell.status = status
  196. }
  197. }
  198. wself.chosedCellStatu = status
  199. }
  200. prepareCatoryList()
  201. }
  202. required init?(coder: NSCoder) {
  203. fatalError("init(coder:) has not been implemented")
  204. }
  205. //MARK: - 请求数据
  206. func prepareCatoryList() {
  207. let categoryListUrl = "produce-center/bgm/app/getAllBgmCates"
  208. BFNetRequestAdaptor.postRequestData(url: onlinePQTvApi + categoryListUrl, parames: [:], commonParams: commonParams(), encoding: JSONEncoding.default, isJsonEncodingNormal: false, timeoutInterval: 15) {[weak self] response, _, error, _ in
  209. guard let wself = self else { return }
  210. var tagsList = [MusicCategoryModel]()
  211. if response is NSNull || response == nil {
  212. cShowHUB(superView: nil, msg: "音乐分类获取失败")
  213. return
  214. }
  215. let tagsTempArr = response as? [[String: Any]]
  216. if tagsTempArr != nil, (tagsTempArr?.count ?? 0) > 0 {
  217. for dict in tagsTempArr! {
  218. let cid = (dict["cateId"] as? Int64 ?? 0)
  219. if cid > 0 {
  220. let tempMusic = MusicCategoryModel(id: cid, name: dict["cateName"] as? String)
  221. tagsList.append(tempMusic)
  222. }
  223. }
  224. }
  225. let tempMusic = MusicCategoryModel(id: -1, name: "")
  226. tagsList.insert(tempMusic, at: 0)
  227. wself.categories.append(contentsOf: tagsList)
  228. wself.categoryView.reloadData()
  229. if wself.categories.count > 1 {
  230. wself.categorySelectIndex = 1
  231. wself.getMusicsForCategory()
  232. }
  233. }
  234. }
  235. func getMusicsForCategory() {
  236. let musicListUrl = "produce-center/bgm/pageCateBgm"
  237. let cate = categories[categorySelectIndex]
  238. BFNetRequestAdaptor.postRequestData(url: onlinePQTvApi + musicListUrl, parames: ["cateId": cate.id, "pageNum": cate.page, "pageSize": 100], commonParams: commonParams(), encoding: JSONEncoding.default) {[weak self] response, _, error, _ in
  239. guard let wself = self else { return }
  240. wself.musicTb.mj_footer?.endRefreshing()
  241. if response is NSNull || response == nil {
  242. cShowHUB(superView: nil, msg: "该分类下无音乐")
  243. return
  244. }
  245. if wself.musicDic[cate.id] == nil {
  246. wself.musicDic[cate.id] = [PQVoiceModel]()
  247. }
  248. var musicPageList = [PQVoiceModel]()
  249. let oldDataMusic : [PQVoiceModel] = wself.musicDic[cate.id]!
  250. if let dict = response as? [String: Any] {
  251. if let tempArr = dict["objs"] as? [[String: Any]] , tempArr.count > 0 {
  252. for (_, dic) in tempArr.enumerated() {
  253. let tempMusic = PQVoiceModel(jsonDict: dic)
  254. tempMusic.cacheTagID = cate.id
  255. if tempMusic.endTime <= tempMusic.startTime {
  256. tempMusic.endTime = tempMusic.startTime + 40
  257. }
  258. let haveIndex = oldDataMusic.firstIndex(where: { (music) -> Bool in
  259. music.musicId == tempMusic.musicId
  260. })
  261. if haveIndex == nil {
  262. musicPageList.append(tempMusic)
  263. }
  264. }
  265. }
  266. if musicPageList.count > 0 {
  267. if let ind = wself.categories.firstIndex(where: {$0.id == cate.id}) {
  268. wself.categories[ind].changePage()
  269. }
  270. wself.musicDic[cate.id]!.append(contentsOf: musicPageList)
  271. if cate.id == wself.categories[wself.categorySelectIndex].id{
  272. wself.musicTb.reloadData()
  273. }
  274. }
  275. if (dict["totalSize"] as? Int64 ?? 0) <= (dict["offset"] as? Int64 ?? -1) {
  276. // 不再需要数据拉新
  277. }
  278. }
  279. }
  280. }
  281. @objc func valuChange(slid:UISlider) {
  282. musicVolume = Int(slid.value * 100)
  283. }
  284. func uiForChangeVolume() {
  285. progressL.text = String(format: "%d%%", musicVolume)
  286. var frame = progressL.frame
  287. frame.origin.x = slidV.x - 7 + ((slidV.width - 28) / 100.0) * CGFloat(musicVolume)
  288. progressL.frame = frame
  289. playManager.player.volume = Float(musicVolume) / 100.0
  290. chosedMusic?.volume = musicVolume
  291. }
  292. func configCategoriesView(){
  293. categoryView.reloadData()
  294. }
  295. func choseCell() -> BFMuicInfoCell? {
  296. if let indpx = chosedIndexPath, let cell = musicTb.cellForRow(at: indpx) as? BFMuicInfoCell {
  297. return cell
  298. }
  299. return nil
  300. }
  301. @objc func btnAction(btn:UIButton) {
  302. playManager.pause()
  303. choseCell()?.status = .pause
  304. switch btn.tag{
  305. case 1001:
  306. clickBtnAction?(.cancle)
  307. chosedMusic?.isSelected = false
  308. chosedMusic = nil
  309. choseCell()?.status = .normal
  310. chosedIndexPath = nil
  311. chosedCellStatu = .normal
  312. case 1002, 1004:
  313. clickBtnAction?(.sure)
  314. case 1003:
  315. clickBtnAction?(.search)
  316. default:break
  317. }
  318. }
  319. func reloadView() {
  320. }
  321. // 禁用音乐
  322. func inhibitChooseMusic() {
  323. chosedMusic = nil
  324. playManager.pause(.normal)
  325. // choseCell()?.status = .normal
  326. choseCell()?.changeSelected(false)
  327. chosedIndexPath = nil
  328. }
  329. func refreshCurrSelectModel(mod:PQVoiceModel?) {
  330. chosedMusic = mod
  331. if let mod = mod {
  332. mod.isSelected = true
  333. }
  334. musicTb.reloadData()
  335. }
  336. }
  337. extension BFChooseMusicView:UITableViewDelegate, UITableViewDataSource {
  338. func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  339. let cell = tableView.dequeueReusableCell(withIdentifier: "BFMuicInfoCell", for: indexPath) as! BFMuicInfoCell
  340. cell.selectionStyle = .none
  341. cell.cutCallBack = {[weak self, weak cell] in
  342. guard let wself = self else { return }
  343. wself.playManager.pause()
  344. if let data = cell?.data {
  345. wself.cutActionCallback?(data)
  346. }
  347. }
  348. let tagid = categories[categorySelectIndex].id
  349. if let arr = musicDic[tagid] {
  350. cell.data = arr[indexPath.row]
  351. if cell.data?.isSelected ?? false {
  352. chosedIndexPath = indexPath
  353. chosedMusic = cell.data
  354. // cell.status = chosedCellStatu
  355. tableView.selectRow(at: indexPath, animated: false, scrollPosition: UITableView.ScrollPosition.none)
  356. cell.status = .pause
  357. playManager.replaceCurrentItem(musicPath: chosedMusic?.musicPath)
  358. }else{
  359. cell.status = .normal
  360. }
  361. }
  362. return cell
  363. }
  364. func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
  365. return 64
  366. }
  367. func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  368. if categories.count > 1 {
  369. return musicDic[categories[categorySelectIndex].id]?.count ?? 0
  370. }
  371. return 0
  372. }
  373. func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  374. if let cell = tableView.cellForRow(at: indexPath) as? BFMuicInfoCell {
  375. if chosedIndexPath == indexPath {
  376. if cell.status != .pause{
  377. cell.status = .pause
  378. playManager.pause()
  379. }else{
  380. BFLog(1, message: "status: 2")
  381. cell.status = .loading
  382. playManager.play()
  383. }
  384. return
  385. }
  386. chosedMusic?.isSelected = false
  387. chosedMusic = cell.data
  388. chosedMusic?.volume = musicVolume
  389. cell.changeSelected(true)
  390. chosedIndexPath = indexPath
  391. if cell.status == .normal {
  392. if let urlStr = cell.data?.musicPath, let url = URL(string: urlStr){
  393. BFLog(1, message: "歌曲地址: \(url)")
  394. if url.absoluteString != (playManager.player.currentItem?.asset as? AVURLAsset)?.url.absoluteString ?? "b" {
  395. cell.status = .loading
  396. playManager.replaceCurrentItem(musicPath: urlStr)
  397. }else {
  398. cell.status = .playing
  399. }
  400. playManager.play()
  401. }
  402. }else if cell.status == .pause{
  403. BFLog(1, message: "status: 2")
  404. playManager.play()
  405. } else{
  406. playManager.pause()
  407. cell.status = .pause
  408. }
  409. chosedCellStatu = cell.status
  410. }
  411. }
  412. func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
  413. let cell = tableView.cellForRow(at: indexPath) as? BFMuicInfoCell
  414. cell?.status = .normal
  415. cell?.changeSelected(false)
  416. chosedIndexPath = nil
  417. playManager.pause()
  418. }
  419. }
  420. extension BFChooseMusicView : UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{
  421. func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
  422. return categories.count
  423. }
  424. func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
  425. let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "BFMusicCategoryCell", for: indexPath) as! BFMusicCategoryCell
  426. cell.addData(dic: categories[indexPath.row])
  427. cell.isChoosed = (indexPath.row == categorySelectIndex)
  428. return cell
  429. }
  430. func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
  431. let oldCell = collectionView.cellForItem(at: IndexPath(row: categorySelectIndex, section: 0)) as? BFMusicCategoryCell
  432. let newCell = collectionView.cellForItem(at: indexPath) as? BFMusicCategoryCell
  433. if indexPath.row > 0 {
  434. categorySelectIndex = indexPath.row
  435. musicTb.reloadData()
  436. oldCell?.isChoosed = false
  437. newCell?.isChoosed = true
  438. getMusicsForCategory()
  439. }else {
  440. inhibitChooseMusic()
  441. }
  442. }
  443. }
  444. extension BFChooseMusicView: BFFlowLayoutDelegate{
  445. func flowLayout(_ flowLayout: BFCollectionViewFlowLayout, itemHeight indexPath: IndexPath) -> CGFloat {
  446. var itemWidth = 30.0
  447. if let title = categories[indexPath.row].name, title.count > 0{
  448. let ww = title.textAutoWidth(height: 20, font: UIFont.systemFont(ofSize: 15, weight: .regular))
  449. itemWidth = max(30.0, ww)
  450. }
  451. return itemWidth
  452. }
  453. }
  454. extension String{
  455. func textAutoWidth(height:CGFloat, font:UIFont) ->CGFloat{
  456. let string = self as NSString
  457. let origin = NSStringDrawingOptions.usesLineFragmentOrigin
  458. let lead = NSStringDrawingOptions.usesFontLeading
  459. let rect = string.boundingRect(with:CGSize(width:0, height: height), options: [origin,lead],
  460. attributes: [NSAttributedString.Key.font:font],
  461. context:nil)
  462. return rect.width
  463. }
  464. }