PQStuckPointCuttingView.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. //
  2. // PQStuckPointCuttingView.swift
  3. // PQSpeed
  4. //
  5. // Created by SanW on 2021/5/8.
  6. // Copyright © 2021 BytesFlow. All rights reserved.
  7. //
  8. import UIKit
  9. class PQStuckPointCuttingView: UIView {
  10. // 视频时长
  11. private var videoDuration: CGFloat = 0
  12. // 卡点开始时间 默认 0
  13. private var stuckPointStartTime: CGFloat = 0
  14. // 卡点结束时间
  15. private var stuckPointEndTime: CGFloat = 0
  16. // 裁剪开始时间 默认 0
  17. private var cutStartTime: CGFloat = 0
  18. // 裁剪结束时间
  19. private var cutEndTime: CGFloat = 0
  20. /// 裁剪结束最终的开始时间
  21. private var cutFinishedStartTime: CGFloat {
  22. (scrollView.contentOffset.x / perSecondWidth) + (((scrollView.frame.width - videoCropView.frame.width) / 2 + 15) / perSecondWidth) + cutStartTime
  23. }
  24. /// 裁剪结束最终的结束时间
  25. private var cutFinishedEndTime: CGFloat {
  26. cutFinishedStartTime + (cutEndTime - cutStartTime)
  27. }
  28. // 播放进度
  29. private var videoProgress: CGFloat = 0
  30. // 最大时长 默认 40s
  31. private var maxCutTime: CGFloat = cDefaultMargin * 4
  32. // 最小时长 默认 10s
  33. private var minCutTime: CGFloat = 10
  34. /// 时间间隔
  35. private var timeRange: CGFloat = cDefaultMargin
  36. /// 刻度高
  37. private var rateHeight: CGFloat = cDefaultMargin * 5
  38. /// 时间线宽
  39. private var timeLineWidth: CGFloat = 35
  40. /// 时间线间隔
  41. private var timeLineMargin: CGFloat = 35
  42. /// 时间线高
  43. private var timeHeight: CGFloat = cDefaultMargin * 4
  44. /// 频率宽
  45. private var frequencyWidth: CGFloat = 1
  46. /// 频率间隔
  47. private var frequencyMargin: CGFloat = 4
  48. /// 左右间隔
  49. private var margin: CGFloat = cDefaultMargin * 3
  50. /// 每秒宽度
  51. private var perSecondWidth: CGFloat = 0
  52. /// 滑动区域大小
  53. private var contentWidth: CGFloat = 0
  54. /// 拖拽改变实时的回调
  55. var videoRangeDidChanged: ((_ startTime: CGFloat, _ endTime: CGFloat) -> Void)?
  56. /// 进度改变实时的回调
  57. var videoProgressDidChanged: ((_ progress: CGFloat) -> Void)?
  58. /// 拖缀结束的回调 type - 1-拖动左边裁剪结束 2--拖动右边裁剪结束 3-进度条拖动结束 4-滑动结束
  59. var videoDidEndDragging: ((_ type: Int, _ startTime: CGFloat, _ endTime: CGFloat, _ progress: CGFloat) -> Void)?
  60. /// 滚动视图
  61. lazy var scrollView: UIScrollView = {
  62. let scrollView = UIScrollView(frame: bounds)
  63. scrollView.showsVerticalScrollIndicator = false
  64. scrollView.showsHorizontalScrollIndicator = false
  65. scrollView.bounces = false
  66. scrollView.delegate = self
  67. if #available(iOS 11.0, *) {
  68. scrollView.contentInsetAdjustmentBehavior = .never
  69. } else {
  70. // automaticallyAdjustsScrollViewInsets = false
  71. }
  72. return scrollView
  73. }()
  74. lazy var rateView: UIView = {
  75. let rateView = UIView(frame: CGRect(x: 0, y: videoCropView.frame.minY + 16.5, width: scrollView.contentSize.width, height: rateHeight))
  76. rateView.backgroundColor = PQBFConfig.shared.otherTintColor
  77. return rateView
  78. }()
  79. lazy var videoCropView: PQVideoCutingOprateView = {
  80. var originX: CGFloat = perSecondWidth * (stuckPointStartTime + cutStartTime) + margin
  81. if contentWidth > scrollView.frame.width {
  82. originX = perSecondWidth * (stuckPointStartTime - cutStartTime) + margin
  83. }
  84. let videoCropView: PQVideoCutingOprateView = PQVideoCutingOprateView(frame: CGRect(x: margin, y: frame.height - 90, width: contentWidth - margin * 2, height: 90), duration: videoDuration, startTime: cutStartTime, endTime: cutEndTime, minDuration: minCutTime, maxDuration: maxCutTime)
  85. videoCropView.cutRangeDidChanged = { [weak self] cutStartTime, cutEndTime, _ in
  86. self?.cutStartTime = cutStartTime
  87. self?.cutEndTime = cutEndTime
  88. self?.videoProgress = 0
  89. // 计算当前时间
  90. if self?.videoRangeDidChanged != nil {
  91. self?.videoRangeDidChanged!(cutStartTime, cutEndTime)
  92. }
  93. }
  94. videoCropView.progressDidChanged = { [weak self] progress in
  95. self?.videoProgress = progress
  96. if self?.videoProgressDidChanged != nil {
  97. self?.videoProgressDidChanged!(progress)
  98. }
  99. }
  100. videoCropView.didEndDragging = { [weak self] type, startTime, endTime, progress in
  101. self?.videoProgress = 0
  102. if self?.videoDidEndDragging != nil {
  103. self?.videoDidEndDragging!(type, startTime, endTime, progress)
  104. }
  105. }
  106. return videoCropView
  107. }()
  108. override private init(frame: CGRect) {
  109. super.init(frame: frame)
  110. }
  111. required init?(coder _: NSCoder) {
  112. fatalError("init(coder:) has not been implemented")
  113. }
  114. init(frame: CGRect, duration: CGFloat, startTime: CGFloat, endTime: CGFloat) {
  115. super.init(frame: frame)
  116. videoDuration = duration
  117. if videoDuration < maxCutTime {
  118. maxCutTime = videoDuration
  119. }
  120. // 更新卡点值
  121. updateEndTime(startTime: startTime, endTime: endTime)
  122. }
  123. /// 更新卡点值
  124. /// - Parameter endTime: <#endTime description#>
  125. /// - Returns: <#description#>
  126. func updateEndTime(startTime: CGFloat, endTime: CGFloat) {
  127. stuckPointStartTime = startTime
  128. stuckPointEndTime = endTime
  129. if (stuckPointEndTime - stuckPointStartTime) > maxCutTime {
  130. stuckPointEndTime = stuckPointStartTime + maxCutTime
  131. }
  132. cutStartTime = startTime
  133. cutEndTime = endTime
  134. if cutEndTime > videoDuration {
  135. cutEndTime = videoDuration
  136. }
  137. backgroundColor = PQBFConfig.shared.styleBackGroundColor
  138. addSubview(scrollView)
  139. addData()
  140. }
  141. func addData() {
  142. perSecondWidth = (timeLineWidth + timeLineMargin) / timeRange
  143. frequencyMargin = perSecondWidth - frequencyWidth
  144. let tempWidth = videoDuration * perSecondWidth + timeLineWidth
  145. let totalCount = Int(videoDuration / timeRange)
  146. // let remainder = videoDuration - (CGFloat(totalCount) * timeRange)
  147. // if remainder != 0 {
  148. // totalCount = totalCount + 1
  149. // }
  150. if (tempWidth + margin * 2) < scrollView.frame.width {
  151. margin = (scrollView.frame.width - tempWidth) / 2
  152. }
  153. contentWidth = tempWidth + margin * 2
  154. if contentWidth < scrollView.frame.width {
  155. contentWidth = scrollView.frame.width
  156. }
  157. scrollView.contentSize = CGSize(width: contentWidth, height: scrollView.frame.height)
  158. for index in 0 ... totalCount {
  159. let titleLab = UILabel(frame: CGRect(x: CGFloat(index) * (timeLineWidth + timeLineMargin) + margin, y: 0, width: timeLineWidth, height: timeHeight))
  160. titleLab.font = UIFont.systemFont(ofSize: 12)
  161. titleLab.textAlignment = .center
  162. titleLab.numberOfLines = 2
  163. titleLab.textColor = UIColor.hexColor(hexadecimal: "#999999")
  164. titleLab.text = "\((Float64(index) * Float64(timeRange)).formatDurationToHMS())\n·"
  165. scrollView.addSubview(titleLab)
  166. }
  167. if perSecondWidth > 0, cutEndTime > 0, cutStartTime < cutEndTime {
  168. /// 处理音频频率
  169. configVoiceFrequency()
  170. // 每秒占有宽度
  171. if scrollView.frame.width < scrollView.contentSize.width {
  172. var offsetX = perSecondWidth * (stuckPointStartTime + (stuckPointEndTime - stuckPointStartTime) / 2) - scrollView.frame.width / 2 + timeLineWidth
  173. if offsetX < 0 {
  174. offsetX = 0
  175. }
  176. if offsetX > (scrollView.contentSize.width - scrollView.frame.width) {
  177. offsetX = (scrollView.contentSize.width - scrollView.frame.width)
  178. }
  179. scrollView.contentOffset = CGPoint(x: offsetX, y: 0)
  180. }
  181. scrollView.addSubview(rateView)
  182. scrollView.addSubview(videoCropView)
  183. }
  184. }
  185. /// 处理音频频率
  186. /// - Returns: <#description#>
  187. func configVoiceFrequency() {
  188. let waveTotalCount = Int(videoDuration) / cFrequency.count
  189. var remainder = Int(ceil(videoDuration - CGFloat(waveTotalCount * cFrequency.count)))
  190. var totalWave: [CGFloat] = Array<CGFloat>.init()
  191. for _ in 0 ..< waveTotalCount {
  192. totalWave = totalWave + cFrequency
  193. }
  194. if remainder > cFrequency.count - 1 {
  195. remainder = cFrequency.count - 1
  196. }
  197. if remainder > 0 {
  198. totalWave = totalWave + cFrequency[0 ... remainder]
  199. }
  200. createWave(waveArr: totalWave)
  201. }
  202. /// 生成波纹
  203. /// - Parameter waveArr: <#waveArr description#>
  204. /// - Returns: <#description#>
  205. func createWave(waveArr: [CGFloat]) {
  206. for (i, power) in waveArr.enumerated() {
  207. // 画布高度
  208. let hight: CGFloat = rateView.frame.height
  209. // 开始 Y 值
  210. var startY: CGFloat = (hight - power) / 2.0
  211. if startY < 0 { startY = 0 }
  212. // 结束 Y 值
  213. var endY: CGFloat = startY + power
  214. if endY > CGFloat(hight) { endY = hight }
  215. // 线的路径
  216. let linePath = UIBezierPath()
  217. // 起点
  218. let originX: CGFloat = CGFloat(i * Int(frequencyWidth + frequencyMargin)) + margin + timeLineWidth / 2
  219. linePath.move(to: CGPoint(x: originX, y: startY))
  220. // 终点
  221. linePath.addLine(to: CGPoint(x: originX, y: endY))
  222. let lineLayer = CAShapeLayer()
  223. lineLayer.lineWidth = frequencyWidth
  224. lineLayer.strokeColor = UIColor.hexColor(hexadecimal: "#999999").cgColor
  225. lineLayer.path = linePath.cgPath
  226. lineLayer.fillColor = UIColor.black.cgColor
  227. rateView.layer.insertSublayer(lineLayer, at: 0)
  228. }
  229. }
  230. deinit {
  231. BFLog(message: "卡点裁剪-裁剪视图销毁")
  232. }
  233. }
  234. // MARK: - scrollView滑动代理
  235. /// scrollView滑动代理
  236. extension PQStuckPointCuttingView: UIScrollViewDelegate {
  237. func scrollViewDidScroll(_: UIScrollView) {
  238. // 计算当前开始时间
  239. // if scrollView.contentOffset.x > margin {
  240. // if videoRangeDidChanged != nil {
  241. // videoRangeDidChanged!(cutFinishedStartTime, cutFinishedEndTime)
  242. // }
  243. // NSObject.cancelPreviousPerformRequests(withTarget: self)
  244. // perform(#selector(scrollViewDidEndScrollingAnimation(scrollView:)), with: nil, afterDelay: 0.1)
  245. // }
  246. }
  247. @objc func scrollViewDidEndScrollingAnimation(scrollView _: UIScrollView) {
  248. BFLog(message: "拖拽结束 - 回调")
  249. // videoProgress = 0
  250. // if videoDidEndDragging != nil {
  251. // videoDidEndDragging!(3, cutFinishedStartTime, cutFinishedEndTime, videoProgress)
  252. // }
  253. }
  254. }