|
@@ -6,15 +6,96 @@
|
|
//
|
|
//
|
|
|
|
|
|
import Foundation
|
|
import Foundation
|
|
|
|
+import UIKit
|
|
|
|
+import BFUIKit
|
|
|
|
+import BFMediaKit
|
|
|
|
+import BFCommonKit
|
|
|
|
+import BFNetRequestKit
|
|
|
|
+
|
|
|
|
|
|
enum BFChooseMusicViewClickType {
|
|
enum BFChooseMusicViewClickType {
|
|
- case sure, cancle
|
|
|
|
|
|
+ case sure, cancle, search
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+
|
|
class BFChooseMusicView: UIView {
|
|
class BFChooseMusicView: UIView {
|
|
|
|
|
|
var clickBtnAction: ((BFChooseMusicViewClickType) -> Void)?
|
|
var clickBtnAction: ((BFChooseMusicViewClickType) -> Void)?
|
|
|
|
|
|
|
|
+ // 各类别音乐列表,以类别id为key
|
|
|
|
+ var musicArray = [Int64 : [PQVoiceModel]]()
|
|
|
|
+
|
|
|
|
+ // 选择的音乐类别
|
|
|
|
+ var categorySelectIndex : Int = 1
|
|
|
|
+
|
|
|
|
+ // 音乐类别
|
|
|
|
+ var categories = [PQStuckPointMusicTagsModel]()
|
|
|
|
+
|
|
|
|
+ // 选中的音乐
|
|
|
|
+ var chosedMusic : PQVoiceModel?
|
|
|
|
+
|
|
|
|
+ var chosedCell : BFMuicInfoCell?
|
|
|
|
+
|
|
|
|
+ var loadedTimeRangesObserver : NSKeyValueObservation?
|
|
|
|
+
|
|
|
|
+ // 试听音乐
|
|
|
|
+ let player:AVPlayer = {
|
|
|
|
+ let p = AVPlayer(playerItem: nil)
|
|
|
|
+ return p
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+ 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.tableFooterView = UIView()
|
|
|
|
+ tb.backgroundColor = .clear
|
|
|
|
+ return tb
|
|
|
|
+ }()
|
|
|
|
+
|
|
override init(frame: CGRect) {
|
|
override init(frame: CGRect) {
|
|
super.init(frame: frame)
|
|
super.init(frame: frame)
|
|
|
|
|
|
@@ -33,25 +114,304 @@ class BFChooseMusicView: UIView {
|
|
let sureBtn = UIButton()
|
|
let sureBtn = UIButton()
|
|
sureBtn.tag = 1002
|
|
sureBtn.tag = 1002
|
|
sureBtn.frame = CGRect(x: width - 35 - 18, y: 17, width: 35, height: 24)
|
|
sureBtn.frame = CGRect(x: width - 35 - 18, y: 17, width: 35, height: 24)
|
|
- sureBtn.setTitle("取消", for: .normal)
|
|
|
|
- sureBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#616161"), for: .normal)
|
|
|
|
|
|
+ sureBtn.setTitle("确定", for: .normal)
|
|
|
|
+ sureBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#389AFF"), for: .normal)
|
|
sureBtn.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .regular)
|
|
sureBtn.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .regular)
|
|
sureBtn.addTarget(self, action: #selector(btnAction(btn:)), for: .touchUpInside)
|
|
sureBtn.addTarget(self, action: #selector(btnAction(btn:)), for: .touchUpInside)
|
|
addSubview(sureBtn)
|
|
addSubview(sureBtn)
|
|
|
|
|
|
|
|
+ let line1 = UIView()
|
|
|
|
+ line1.backgroundColor = UIColor.hexColor(hexadecimal: "#272727")
|
|
|
|
+ line1.frame = CGRect(x: 0, y: 50, 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)
|
|
|
|
+
|
|
|
|
+ loadedTimeRangesObserver = player.observe(\AVPlayer.currentItem?.loadedTimeRanges, options: [.new, .initial]) { [weak self] (player, change) in
|
|
|
|
+
|
|
|
|
+ DispatchQueue.main.async {[weak self] in
|
|
|
|
+ guard let wself = self else { return }
|
|
|
|
+
|
|
|
|
+ guard let ranges = change.newValue as? [CMTimeRange] else { return }
|
|
|
|
+ if let cell = wself.chosedCell, let totalDur = player.currentItem?.duration, totalDur.isValid, CMTimeCompare(ranges.first?.duration ?? .zero, totalDur) >= 0{
|
|
|
|
+ if (player.currentItem?.asset as? AVURLAsset)?.url.absoluteString ?? "b" == cell.data?.musicPath ?? "a" {
|
|
|
|
+ if cell.status == .loading{
|
|
|
|
+ cell.status = .playing
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ BFLog(1, message: "开始播放音乐:\(ranges.first?.start.seconds ?? 0), dur:\( ranges.first?.duration.seconds ?? 0), tot:\(player.currentItem?.duration.seconds ?? 0)")
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ prepareData()
|
|
|
|
+
|
|
}
|
|
}
|
|
|
|
|
|
required init?(coder: NSCoder) {
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ func prepareData() {
|
|
|
|
+ BFNetRequestAdaptor.postRequestData(url: PQENVUtil.shared.longvideoapi + stuckPointMusicCategoryUrl, parames: ["parentTagId": 0], commonParams: commonParams(), isJsonEncodingNormal: true, timeoutInterval: 15) {[weak self] response, _, error, _ in
|
|
|
|
+ guard let wself = self else { return }
|
|
|
|
+
|
|
|
|
+ var tagsList = Array<PQStuckPointMusicTagsModel>.init()
|
|
|
|
+ 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 tempMusic = PQStuckPointMusicTagsModel(jsonDict: dict)
|
|
|
|
+ tempMusic.parentTagId = 0
|
|
|
|
+ tagsList.append(tempMusic)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ tagsList.first?.isSelected = true
|
|
|
|
+
|
|
|
|
+ let tempMusic = PQStuckPointMusicTagsModel()
|
|
|
|
+ tempMusic.tagId = -1
|
|
|
|
+ tagsList.insert(tempMusic, at: 0)
|
|
|
|
+ wself.categories.append(contentsOf: tagsList)
|
|
|
|
+ wself.categoryView.reloadData()
|
|
|
|
+ if wself.categories.count > 1 {
|
|
|
|
+ wself.categorySelectIndex = 1
|
|
|
|
+ if let firstTagid = wself.categories[1].tagId{
|
|
|
|
+ wself.getMusicsForCategory(tagId:firstTagid, pageNum: 1)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ func getMusicsForCategory(tagId:Int64, pageNum:Int64) {
|
|
|
|
+ BFNetRequestAdaptor.postRequestData(url: PQENVUtil.shared.longvideoapi + stuckPointMusicPageUrl, parames: ["tagId": tagId, "parentTagId": 0, "pageNum": pageNum, "pageSize": 20], commonParams: commonParams()) {[weak self] response, _, error, _ in
|
|
|
|
+ guard let wself = self else { return }
|
|
|
|
+
|
|
|
|
+ var musicPageList = [PQVoiceModel]()
|
|
|
|
+ if response is NSNull || response == nil {
|
|
|
|
+ cShowHUB(superView: nil, msg: "该分类下无音乐")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ if wself.musicArray[tagId] == nil {
|
|
|
|
+ wself.musicArray[tagId] = [PQVoiceModel]()
|
|
|
|
+ }
|
|
|
|
+ let oldDataMusic : [PQVoiceModel] = wself.musicArray[tagId]!
|
|
|
|
+
|
|
|
|
+ if let tempArr = response as? [[String: Any]], tempArr.count > 0 {
|
|
|
|
+ for (_, dict) in tempArr.enumerated() {
|
|
|
|
+ let tempMusic = PQVoiceModel(jsonDict: dict)
|
|
|
|
+ tempMusic.cacheTagID = tagId
|
|
|
|
+ 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 {
|
|
|
|
+ wself.musicArray[tagId]!.append(contentsOf: musicPageList)
|
|
|
|
+ if tagId == wself.categories[wself.categorySelectIndex].tagId{
|
|
|
|
+ wself.musicTb.reloadData()
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @objc func valuChange(slid:UISlider) {
|
|
|
|
+ let progress = slid.value
|
|
|
|
+ let num = (Int)(progress * 100)
|
|
|
|
+ progressL.text = String(format: "%d%%", num)
|
|
|
|
+ var frame = progressL.frame
|
|
|
|
+ frame.origin.x = slidV.x - 7 + ((slidV.width - 28) / 100.0) * CGFloat(num)
|
|
|
|
+ progressL.frame = frame
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ func configCategoriesView(){
|
|
|
|
+ categoryView.reloadData()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
|
|
@objc func btnAction(btn:UIButton) {
|
|
@objc func btnAction(btn:UIButton) {
|
|
- if btn.tag == 1001 {
|
|
|
|
- //取消
|
|
|
|
|
|
+
|
|
|
|
+ player.pause()
|
|
|
|
+
|
|
|
|
+ switch btn.tag{
|
|
|
|
+ case 1001:
|
|
clickBtnAction?(.cancle)
|
|
clickBtnAction?(.cancle)
|
|
- }else {
|
|
|
|
|
|
+ case 1002:
|
|
clickBtnAction?(.sure)
|
|
clickBtnAction?(.sure)
|
|
|
|
+ case 1003:
|
|
|
|
+ clickBtnAction?(.search)
|
|
|
|
+ default:break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ func reloadView() {
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+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 = {
|
|
|
|
+ // 调用裁剪UIView
|
|
|
|
+ }
|
|
|
|
+ if let tagid = categories[categorySelectIndex].tagId, let arr = musicArray[tagid] {
|
|
|
|
+ cell.data = arr[indexPath.row]
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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 musicArray[categories[categorySelectIndex].tagId ?? 0]?.count ?? 0
|
|
|
|
+ }
|
|
|
|
+ return 0
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
|
|
|
+ if let cell = tableView.cellForRow(at: indexPath) as? BFMuicInfoCell {
|
|
|
|
+
|
|
|
|
+ chosedMusic = cell.data
|
|
|
|
+ chosedMusic?.isSelected = true
|
|
|
|
+ cell.changeSelected(true)
|
|
|
|
+
|
|
|
|
+ chosedCell = cell
|
|
|
|
+
|
|
|
|
+ if cell.status == .normal {
|
|
|
|
+ if let urlStr = cell.data?.musicPath, let url = URL(string: urlStr){
|
|
|
|
+ BFLog(1, message: "歌曲地址: \(url)")
|
|
|
|
+ if url.absoluteString != (player.currentItem?.asset as? AVURLAsset)?.url.absoluteString ?? "b" {
|
|
|
|
+ player.replaceCurrentItem(with: AVPlayerItem(url: url))
|
|
|
|
+ cell.status = .loading
|
|
|
|
+ }else {
|
|
|
|
+ cell.status = .playing
|
|
|
|
+ }
|
|
|
|
+ player.play()
|
|
|
|
+ }
|
|
|
|
+ }else if cell.status == .pause{
|
|
|
|
+ player.play()
|
|
|
|
+ } else{
|
|
|
|
+ player.pause()
|
|
|
|
+ cell.status = .pause
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
|
|
|
|
+ let cell = tableView.cellForRow(at: indexPath) as? BFMuicInfoCell
|
|
|
|
+ cell?.status = .normal
|
|
|
|
+ cell?.changeSelected(false)
|
|
|
|
+
|
|
|
|
+ chosedCell = nil
|
|
|
|
+ player.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(tagId: categories[categorySelectIndex].tagId ?? 0, pageNum: 1)
|
|
|
|
+
|
|
|
|
+ }else {
|
|
|
|
+ // 取消选歌
|
|
|
|
+ chosedMusic = nil
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+extension BFChooseMusicView: BFFlowLayoutDelegate{
|
|
|
|
+
|
|
|
|
+ func flowLayout(_ flowLayout: BFCollectionViewFlowLayout, itemHeight indexPath: IndexPath) -> CGFloat {
|
|
|
|
+
|
|
|
|
+ var itemWidth = 30.0
|
|
|
|
+
|
|
|
|
+ if let title = categories[indexPath.row].tagName, 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
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|