Pārlūkot izejas kodu

起点裁剪界面

jsonwang 3 gadi atpakaļ
vecāks
revīzija
d3045c8251

+ 416 - 0
BFRecordScreenKit/Classes/RecordScreen/View/BFMusicCutView.swift

@@ -0,0 +1,416 @@
+//
+//  BFMusicCutView.swift
+//  BFRecordScreenKit
+//
+//  Created by ak on 2022/3/4.
+//  功能:音乐裁剪界面,设置开始播放位置
+ 
+import BFCommonKit
+import BFMediaKit
+import BFUIKit
+import Foundation
+import UIKit
+
+// 操作动作类型
+public enum MusiceCutActionType: Int {
+    case MusiceCutActionCancel = 1 // 取消
+    case MusiceCutActionConfirm = 2 // 确认
+
+}
+
+class BFMusicCutView: UIView ,UIGestureRecognizerDelegate {
+    var waveLayers: [CAShapeLayer] = Array<CAShapeLayer>.init()
+    // 裁剪时间回调
+    var cutTimeHandle: ((_ startTime: Float64, _ endTime: Float64, _ bgmData: PQVoiceModel?) -> Void)?
+    
+    let normalMargin: CGFloat = cDefaultMargin * 2
+    
+    //记录设置的起点, 在点击确认后会设置 bgmdata 的 开始时间为本值。
+    var startCMTime: CMTime = .zero // 开始时间
+    
+    //播放音乐
+    lazy var avPlayer: AVPlayer = {
+        let avPlayer = AVPlayer()
+        PQNotification.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: avPlayer.currentItem, queue: .main) { [weak self] notify in
+            BFLog(message: "AVPlayerItemDidPlayToEndTime = \(notify)")
+            guard let wself = self else { return }
+            
+            avPlayer.seek(to: wself.startCMTime)
+            avPlayer.play()
+            wself.configPlayProgress(currentTime: wself.startCMTime.seconds)
+        }
+        PQNotification.addObserver(forName: .AVPlayerItemNewErrorLogEntry, object: avPlayer.currentItem, queue: .main) { notify in
+            BFLog(message: "AVPlayerItemNewErrorLogEntry = \(notify)")
+        }
+        PQNotification.addObserver(forName: .AVPlayerItemFailedToPlayToEndTime, object: avPlayer.currentItem, queue: .main) { notify in
+            BFLog(message: "AVPlayerItemFailedToPlayToEndTime = \(notify)")
+        }
+        PQNotification.addObserver(forName: .AVPlayerItemPlaybackStalled, object: avPlayer.currentItem, queue: .main) { notify in
+            BFLog(message: "AVPlayerItemPlaybackStalled = \(notify)")
+        }
+        avPlayer.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: CMTimeScale(playerTimescale)), queue: .main) { [weak self] _ in
+            guard let wself = self else { return }
+            
+            let currentTime = CMTimeGetSeconds(avPlayer.currentItem?.currentTime() ?? CMTime.zero)
+            wself.configPlayProgress(currentTime: currentTime)
+        }
+        avPlayer.volume = 1.0
+        return avPlayer
+    }()
+
+    
+    // 总时长
+    lazy var totalTimeLab: UILabel = {
+        let totalTimeLab = UILabel()
+        totalTimeLab.backgroundColor = .red
+        totalTimeLab.textColor = UIColor.white
+        totalTimeLab.font = UIFont.systemFont(ofSize: 13)
+        totalTimeLab.text = "00:00 / 00:00"
+        return totalTimeLab
+    }()
+    
+    //放指针的背景图
+    lazy var panCutBackView: UIView = {
+        let panCutBackView = UIView()
+        panCutBackView.backgroundColor = .clear
+        let panGes = UIPanGestureRecognizer(target: self, action: #selector(panClick(ges:)))
+        panCutBackView.addGestureRecognizer(panGes)
+        return panCutBackView
+    }()
+
+    //指针
+    lazy var cutRemindView: UIView = {
+        let cutRemindView = UIView()
+        cutRemindView.backgroundColor = UIColor.hexColor(hexadecimal: "#389AFF")
+        cutRemindView.addCorner(corner: 2)
+        cutRemindView.frame = CGRect(x: (panCutBackView.frame.width - 2) / 2, y: 0, width: 2, height: panCutBackView.frame.height)
+        return cutRemindView
+    }()
+    
+    //水波纹的背景
+    lazy var progressImage: UIImageView = {
+        let progressImage = UIImageView()
+        progressImage.frame = CGRect(x: 20, y: currentProgressLab.frame.maxY + 2, width: cScreenWidth - 20 * 2, height: cDefaultMargin * 6)
+        return progressImage
+    }()
+    
+    //显示进度
+    lazy var currentProgressLab: UILabel = {
+        let currentProgressLab = UILabel(frame: CGRect(x: cDefaultMargin * 3, y: cDefaultMargin * 6, width: cDefaultMargin * 6, height: cDefaultMargin * 2))
+        currentProgressLab.textColor = UIColor.hexColor(hexadecimal: "#616161")
+        currentProgressLab.font = UIFont.systemFont(ofSize: 13)
+        currentProgressLab.text = "00:00"
+        return currentProgressLab
+    }()
+
+    // 操作板背景
+    let backView = UIButton()
+ 
+    // 操作回调  isSame 是否点击的是同一个人,进行状态切换使用
+    public var MusiceCutCallBack: ((_ actionType: MusiceCutActionType, _ selectVoice: PQVoiceModel?,_ isSame:Bool ) -> Void)?
+ 
+    // 当前选择的音乐
+    public var bgmData: PQVoiceModel? {
+        didSet {
+            configPlayProgress(currentTime: bgmData?.currentTime ?? 0)
+            //默认进来播放音乐
+            playBGM()
+            currentProgressLab.center.x = panCutBackView.center.x
+        }
+    }
+    
+    lazy var titleL : UILabel = {
+        let l = UILabel()
+        l.text = "起播点"
+        l.textAlignment = .center
+        l.textColor = .white
+        l.font = UIFont.systemFont(ofSize: 17, weight: .medium)
+        
+        return l
+    }()
+    
+    // 删除 btn
+    lazy var deleteBtn: UIButton = {
+        let deleteBtn = UIButton()
+        deleteBtn.backgroundColor = .clear
+        deleteBtn.setTitleColor(.white, for: .normal)
+        deleteBtn.titleLabel?.font = UIFont.systemFont(ofSize: 16)
+        deleteBtn.addTarget(self, action: #selector(deleteBtnAction), for: .touchDown)
+
+        deleteBtn.setBackgroundImage(imageInRecordScreenKit(by: "voiceDeleteN"), for: .normal)
+        deleteBtn.setBackgroundImage(imageInRecordScreenKit(by: "voiceDeleteS"), for: .selected)
+        deleteBtn.adjustsImageWhenHighlighted = false
+        return deleteBtn
+    }()
+ 
+    required init?(coder _: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+    override func layoutSubviews() {
+        super.layoutSubviews()
+        backView.addCorner(roundingCorners: [.topLeft, .topRight], corner: 10)
+        addCutViewLayout()
+    }
+    
+    
+    func addCutViewLayout() {
+        let totalWidth = (frame.width - normalMargin * 2 - 6)
+        let itemWidth = totalWidth / CGFloat(Double("\(bgmData?.duration ?? "1")") ?? 0.0)
+        panCutBackView.frame = CGRect(x: (normalMargin - cDefaultMargin * 1.5) + CGFloat(bgmData?.startTime ?? 0) * itemWidth, y: currentProgressLab.frame.maxY + 2, width: cDefaultMargin * 3, height: cDefaultMargin * 6)
+        cutRemindView.frame = CGRect(x: (panCutBackView.frame.width - 2) / 2, y: 0, width: 2, height: panCutBackView.frame.height)
+    }
+    
+    func playBGM(){
+
+        if(bgmData?.musicPath ?? "").count > 0{
+            avPlayer.pause()
+            avPlayer.replaceCurrentItem(with: AVPlayerItem(url: URL(string: (bgmData?.musicPath ?? ""))!))
+            avPlayer.seek(to: (bgmData?.startCMTime ?? .zero))
+            avPlayer.play()
+        }
+    
+    }
+
+
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+
+        backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0)
+        backView.backgroundColor = UIColor.hexColor(hexadecimal: "#121212")
+
+        addSubview(backView)
+        backView.snp.makeConstraints { make in
+            make.right.equalTo(self.snp.right)
+            make.bottom.equalTo(self.snp.bottom)
+            make.width.equalTo(cScreenWidth)
+            make.height.equalTo(220)
+        }
+        backView.setNeedsUpdateConstraints()
+
+        // 取消
+        let cancelBtn = UIButton()
+        cancelBtn.backgroundColor = .clear
+        cancelBtn.setTitle("取消", for: .normal)
+        cancelBtn.setTitleColor(.white, for: .normal)
+        cancelBtn.titleLabel?.font = UIFont.boldSystemFont(ofSize: 17)
+        cancelBtn.addTarget(self, action: #selector(cancelAction), for: .touchDown)
+        backView.addSubview(cancelBtn)
+
+        cancelBtn.snp.makeConstraints { make in
+            make.left.equalToSuperview().offset(18)
+            make.width.equalTo(35)
+            make.height.equalTo(24)
+            make.top.equalToSuperview().offset(18)
+        }
+        
+        backView.addSubview(titleL)
+        titleL.snp.makeConstraints { make in
+            make.centerX.equalToSuperview()
+            make.width.equalTo(54)
+            make.height.equalTo(24)
+            make.top.equalTo(cancelBtn)
+        }
+        
+       
+        
+        // 确认
+        let okBtn = UIButton()
+        okBtn.backgroundColor = .clear
+        okBtn.setTitle("确认", for: .normal)
+        okBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#389AFF"), for: .normal)
+        okBtn.titleLabel?.font = UIFont.systemFont(ofSize: 17)
+        okBtn.addTarget(self, action: #selector(okBtnAction), for: .touchUpInside)
+        backView.addSubview(okBtn)
+        okBtn.snp.makeConstraints { make in
+            make.right.equalToSuperview().offset(-18)
+            make.width.equalTo(35)
+            make.height.equalTo(24)
+            make.top.equalToSuperview().offset(18)
+        }
+        
+        backView.addSubview(totalTimeLab)
+        backView.addSubview(panCutBackView)
+        panCutBackView.addSubview(cutRemindView)
+        backView.addSubview(currentProgressLab)
+        backView.addSubview(progressImage)
+        
+        if (progressImage.layer.sublayers?.count ?? 0) > 0 {
+            let totalCount = progressImage.layer.sublayers?.count ?? 0
+            for index in 0..<Int(totalCount) {
+                (progressImage.layer.sublayers?[index] as? CAShapeLayer)?.strokeColor = UIColor.white.cgColor
+                (progressImage.layer.sublayers?[index] as? CAShapeLayer)?.setNeedsDisplay()
+            }
+        } else {
+            let totalCount = Int(cScreenWidth - normalMargin * 2) / (cFrequency.count * 2)
+            let remainder = Int(cScreenWidth - normalMargin * 2) % (cFrequency.count * 2)
+            var totalWave: [CGFloat] = Array<CGFloat>.init()
+            for _ in 0..<totalCount {
+                totalWave = totalWave + cFrequency
+            }
+            if remainder > 0 {
+                totalWave = totalWave + cFrequency[0...(remainder / 2)]
+            }
+            createWave(waveArr: totalWave)
+        }
+ 
+ 
+    }
+    
+    /// 生成波纹
+    /// - Parameter waveArr: <#waveArr description#>
+    /// - Returns: <#description#>
+    func createWave(waveArr: [CGFloat]) {
+        for (i, power) in waveArr.enumerated() {
+            // 画布高度
+            let hight: CGFloat = progressImage.frame.height
+            // 开始 Y 值
+            var startY: CGFloat = (hight - power) / 2.0
+            if startY < 0 { startY = 0 }
+            // 结束 Y 值
+            var endY: CGFloat = startY + power
+            if endY > CGFloat(hight) { endY = hight }
+            // 线的路径
+            let linePath = UIBezierPath()
+
+            // 起点
+            let originX: CGFloat = CGFloat(i * 2)
+            linePath.move(to: CGPoint(x: originX, y: startY))
+            // 终点
+            linePath.addLine(to: CGPoint(x: originX, y: endY))
+
+            let lineLayer = CAShapeLayer()
+
+            lineLayer.lineWidth = 1
+
+            lineLayer.strokeColor = UIColor.white.cgColor
+
+            lineLayer.path = linePath.cgPath
+            lineLayer.fillColor = UIColor.black.cgColor
+
+            waveLayers.append(lineLayer)
+            progressImage.layer.insertSublayer(lineLayer, at: 0)
+        }
+    }
+    
+    @objc func panClick(ges: UIPanGestureRecognizer) {
+        if ges.state == .changed {
+            let translation = ges.translation(in: backView)
+            let totalWidth = progressImage.frame.width + (cutRemindView.frame.width / 2)
+            let itemWidth = totalWidth / CGFloat(Double("\(bgmData?.duration ?? "1")") ?? 0.0)
+            let maxX = progressImage.frame.maxX - itemWidth - 3
+            let minX = cDefaultMargin * 2 - (cutRemindView.frame.width / 2) - 3
+            let maxWidth = maxX - minX + itemWidth
+            var gesCenx = (ges.view?.center.x ?? 0) + translation.x
+            if gesCenx < minX {
+                gesCenx = minX
+            } else if gesCenx > maxX {
+                gesCenx = maxX
+            }
+            ges.view?.center.x = gesCenx
+            currentProgressLab.center.x = panCutBackView.center.x + cDefaultMargin
+            ges.setTranslation(CGPoint.zero, in: ges.view)
+            let cenx = (ges.view!.center.x - minX)
+            currentProgressLab.text = (Float64(cenx / maxWidth) * (Float64("\(bgmData?.duration ?? "0")") ?? 0.0)).formatDurationToHMS()
+            let startTime = Float64(Int((cenx / maxWidth) * CGFloat(Double("\(bgmData?.duration ?? "0")") ?? 0.0)))
+            let endTime = Float64(Double("\(bgmData?.duration ?? "0")") ?? 0.0)
+            BFLog(message: "裁剪背景音乐 duration = \(bgmData?.duration ?? "0"),startTime = \(startTime),endTime = \(endTime)")
+            bgmData?.currentTime = startTime
+            bgmData?.startTime = startTime
+            bgmData?.endTime = endTime
+//            if cutTimeHandle != nil {
+//                cutTimeHandle!(startTime, endTime, bgmData)
+//            }
+            
+            avPlayer.seek(to: CMTime(value: CMTimeValue(startTime * playerTimescale), timescale: CMTimeScale(playerTimescale)))
+            // 重置波纹
+            resetWave()
+        }
+    }
+    /// 重置波纹
+    /// - Returns: <#description#>
+    func resetWave() {
+        let startTotal = (Float64(waveLayers.count) / (Float64("\(bgmData?.duration ?? "0")") ?? 1)) * Float64(bgmData?.startTime ?? 0)
+        if startTotal.isNaN {
+            return
+        }
+        let totalCount = progressImage.layer.sublayers?.count ?? 0
+        if totalCount > 0 {
+            for index in 0..<Int(totalCount) {
+                if (totalCount - index - 1) < totalCount {
+                    if index < Int(startTotal) {
+                        (progressImage.layer.sublayers?[totalCount - index - 1] as? CAShapeLayer)?.strokeColor = UIColor.hexColor(hexadecimal: "#202020").cgColor
+                    } else {
+                        (progressImage.layer.sublayers?[totalCount - index - 1] as? CAShapeLayer)?.strokeColor = UIColor.white.cgColor
+                    }
+                    (progressImage.layer.sublayers?[totalCount - index - 1] as? CAShapeLayer)?.setNeedsDisplay()
+                }
+            }
+        }
+    }
+    func configPlayProgress(currentTime: Float64) {
+        bgmData?.currentTime = currentTime < 0 ? 0 : currentTime
+        let totalCount = progressImage.layer.sublayers?.count ?? 0
+        // 循环播放时重置
+        if (bgmData?.currentTime ?? 0) == (bgmData?.startTime ?? 0) {
+            // 重置波纹
+            resetWave()
+        }
+        if Float64(bgmData?.currentTime ?? 0) > Float64(bgmData?.startTime ?? 0) {
+            let sdd = (Float64(bgmData?.currentTime ?? 0) - Float64(bgmData?.startTime ?? 0))
+            let total = (Float64(waveLayers.count) / (Float64("\(bgmData?.duration ?? "0")") ?? 1)) * sdd
+            let startTotal = (Float64(waveLayers.count) / (Float64("\(bgmData?.duration ?? "0")") ?? 1)) * Float64(bgmData?.startTime ?? 0)
+
+            if totalCount > 0 {
+                for index in 0..<Int(total) {
+                    var tempIndex = (totalCount - index - 1 - Int(startTotal))
+                    if tempIndex < 0 {
+                        tempIndex = 0
+                    }
+                    if tempIndex >= totalCount - 1 {
+                        tempIndex = totalCount - 1
+                    }
+                    (progressImage.layer.sublayers?[tempIndex] as? CAShapeLayer)?.strokeColor = UIColor.hexColor(hexadecimal: "#389AFF").cgColor
+                    (progressImage.layer.sublayers?[tempIndex] as? CAShapeLayer)?.setNeedsDisplay()
+                }
+            }
+        }
+    }
+
+
+    
+    
+    func show(){
+        isHidden = false
+
+    }
+
+    @objc func hidden() {
+        isHidden = true
+        avPlayer.pause()
+    }
+
+    @objc func cancelAction() {
+        hidden()
+    
+    }
+    
+
+    @objc func okBtnAction() {
+        hidden()
+//        if MusiceCutCallBack != nil {
+//            MusiceCutCallBack!(.MusiceCutActionConfirm, selectVoice,false)
+//        }
+    }
+
+    @objc func deleteBtnAction() {
+   
+//        if MusiceCutCallBack != nil {
+//            MusiceCutCallBack!(.MusiceCutActionDelete, selectVoice,false)
+//        }
+    }
+    
+    
+ 
+}
+