PQVideoCutingOprateView.swift 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. //
  2. // PQVideoCutingOprateView.swift
  3. // PQSpeed
  4. //
  5. // Created by SanW on 2021/5/9.
  6. // Copyright © 2021 BytesFlow. All rights reserved.
  7. //
  8. import UIKit
  9. class PQVideoCutingOprateView: UIView {
  10. // 距离左边间隔
  11. var leftMargin: CGFloat = 0
  12. // 距离右边间隔
  13. var rightMargin: CGFloat = 0
  14. // 距离上边间隔
  15. var topMargin: CGFloat = 14
  16. // 距离下边间隔
  17. var bottomMargin: CGFloat = 20
  18. /// 上下线条的高度
  19. var lineHeight: CGFloat = 3.0
  20. /// 进度宽度
  21. var progressWidth: CGFloat = 12.0
  22. // 左右裁剪操作宽度
  23. var cutingOprateWidth: CGFloat = 15
  24. // 开始时间 默认 0
  25. private var cutStartTime: CGFloat = 0
  26. // 结束时间
  27. private var cutEndTime: CGFloat = 0
  28. // 视频总时长
  29. private var totalDuration: CGFloat = 0
  30. // 最小裁剪大小 默认 10s
  31. private var cutMinDuration: CGFloat = 10
  32. // 最大裁剪大小 默认 40s
  33. private var cutMaxDuration: CGFloat = 40
  34. /// 每秒宽度
  35. private var perSecondWidth: CGFloat = 0
  36. /// 裁剪总时长
  37. private var cutTotalTime: CGFloat = 0
  38. /// 当前滑动的位置
  39. private var preOriginX: CGFloat = 0
  40. /// 当前滑动的view
  41. private var currentPanView: UIView?
  42. private var progress: CGFloat {
  43. if progressView.frame.minX >= (rightOprateView.frame.minX - (((progressView.frame.width - 3) / 2) + 3)) {
  44. return 1
  45. } else {
  46. return ((progressView.frame.minX - (leftOprateView.frame.maxX - ((progressView.frame.width - 3) / 2))) / perSecondWidth) / (cutEndTime - cutStartTime)
  47. }
  48. }
  49. /// 拖缀结束的回调 type - 1-拖动左边裁剪结束 2--拖动右边裁剪结束 3-进度条拖动结束 4-滑动结束
  50. var didEndDragging: ((_ type: Int, _ startTime: CGFloat, _ endTime: CGFloat, _ progress: CGFloat) -> Void)?
  51. /// 裁剪实时回调
  52. var cutRangeDidChanged: ((_ startTime: CGFloat, _ endTime: CGFloat, _ cutTotalTime: CGFloat) -> Void)?
  53. /// 进度回调
  54. var progressDidChanged: ((_ progress: CGFloat) -> Void)?
  55. lazy var durationLabel: UILabel = {
  56. let durationLabel = UILabel()
  57. durationLabel.font = UIFont.systemFont(ofSize: 11, weight: .medium)
  58. durationLabel.backgroundColor = UIColor(red: 238 / 255, green: 0, blue: 81 / 255, alpha: 0.3)
  59. durationLabel.textColor = UIColor.white
  60. durationLabel.textAlignment = .center
  61. return durationLabel
  62. }()
  63. lazy var leftOprateView: UIImageView = {
  64. let leftOprateView = UIImageView(image:UIImage.init().BF_Image(named: "videomk_crop_left"))
  65. leftOprateView.contentMode = .scaleAspectFill
  66. leftOprateView.isUserInteractionEnabled = true
  67. leftOprateView.backgroundColor = UIColor.white
  68. let panGes = UIPanGestureRecognizer(target: self, action: #selector(panGesture(gesture:)))
  69. panGes.maximumNumberOfTouches = 1
  70. panGes.minimumNumberOfTouches = 1
  71. leftOprateView.addGestureRecognizer(panGes)
  72. return leftOprateView
  73. }()
  74. lazy var rightOprateView: UIImageView = {
  75. let rightOprateView = UIImageView(image: UIImage.init().BF_Image(named: "videomk_crop_right"))
  76. rightOprateView.contentMode = .scaleAspectFill
  77. rightOprateView.isUserInteractionEnabled = true
  78. rightOprateView.backgroundColor = UIColor.white
  79. let panGes = UIPanGestureRecognizer(target: self, action: #selector(panGesture(gesture:)))
  80. panGes.maximumNumberOfTouches = 1
  81. panGes.minimumNumberOfTouches = 1
  82. rightOprateView.addGestureRecognizer(panGes)
  83. return rightOprateView
  84. }()
  85. lazy var topLineView: UIImageView = {
  86. let topLineView = UIImageView()
  87. topLineView.backgroundColor = UIColor.white
  88. return topLineView
  89. }()
  90. lazy var bottomLineView: UIImageView = {
  91. let bottomLineView = UIImageView()
  92. bottomLineView.backgroundColor = UIColor.white
  93. return bottomLineView
  94. }()
  95. lazy var progressView: PQCuttingPointView = {
  96. let progressView = PQCuttingPointView(frame: CGRect(x: 0, y: 0, width: progressWidth, height: frame.height))
  97. let panGes = UIPanGestureRecognizer(target: self, action: #selector(panGesture(gesture:)))
  98. panGes.maximumNumberOfTouches = 1
  99. panGes.minimumNumberOfTouches = 1
  100. progressView.addGestureRecognizer(panGes)
  101. return progressView
  102. }()
  103. override private init(frame: CGRect) {
  104. super.init(frame: frame)
  105. }
  106. required init?(coder _: NSCoder) {
  107. fatalError("init(coder:) has not been implemented")
  108. }
  109. init(frame: CGRect, duration: CGFloat, startTime: CGFloat, endTime: CGFloat, minDuration: CGFloat, maxDuration: CGFloat) {
  110. super.init(frame: frame)
  111. isUserInteractionEnabled = true
  112. clipsToBounds = true
  113. backgroundColor = UIColor.clear
  114. totalDuration = duration
  115. cutStartTime = startTime
  116. cutEndTime = endTime
  117. cutMinDuration = minDuration
  118. cutMaxDuration = maxDuration
  119. if cutMaxDuration <= 0 || cutMaxDuration > totalDuration {
  120. cutMaxDuration = totalDuration
  121. }
  122. if cutMinDuration <= 0 || (cutMinDuration > totalDuration) {
  123. cutMinDuration = 7
  124. }
  125. if cutEndTime <= 0 {
  126. cutEndTime = cutStartTime + cutMinDuration
  127. }
  128. perSecondWidth = 7
  129. cutTotalTime = cutEndTime - cutStartTime
  130. configSubview()
  131. BFLog(message: "======\(frame),perSecondWidth = \(perSecondWidth),cutMaxDuration = \(cutMaxDuration * perSecondWidth)")
  132. }
  133. /// 初始化视图
  134. /// - Returns: <#description#>
  135. func configSubview() {
  136. if leftOprateView.superview == nil {
  137. addSubview(leftOprateView)
  138. }
  139. if rightOprateView.superview == nil {
  140. addSubview(rightOprateView)
  141. }
  142. if topLineView.superview == nil {
  143. addSubview(topLineView)
  144. }
  145. if bottomLineView.superview == nil {
  146. addSubview(bottomLineView)
  147. }
  148. if durationLabel.superview == nil {
  149. addSubview(durationLabel)
  150. }
  151. if progressView.superview == nil {
  152. addSubview(progressView)
  153. }
  154. leftOprateView.frame = CGRect(x: leftMargin + perSecondWidth * cutStartTime, y: topMargin, width: cutingOprateWidth, height: frame.height - topMargin - bottomMargin)
  155. rightOprateView.frame = CGRect(x: leftOprateView.frame.maxX + perSecondWidth * (cutEndTime - cutStartTime), y: leftOprateView.frame.minY, width: cutingOprateWidth, height: frame.height - topMargin - bottomMargin)
  156. // 更新子视图布局
  157. updateSubViewFrame()
  158. }
  159. /// 更新子视图布局
  160. /// - Returns: <#description#>
  161. func updateSubViewFrame() {
  162. topLineView.frame = CGRect(x: leftOprateView.frame.maxX, y: leftOprateView.frame.minY, width: rightOprateView.frame.minX - leftOprateView.frame.maxX, height: lineHeight)
  163. bottomLineView.frame = CGRect(x: topLineView.frame.minX, y: leftOprateView.frame.maxY - lineHeight, width: topLineView.frame.width, height: lineHeight)
  164. progressView.frame = CGRect(x: leftOprateView.frame.maxX - ((progressView.frame.width - 3) / 2), y: 0, width: progressWidth, height: frame.height)
  165. durationLabel.frame = CGRect(x: topLineView.frame.minX, y: topLineView.frame.maxY, width: topLineView.frame.width, height: bottomLineView.frame.minY - topLineView.frame.maxY)
  166. durationLabel.text = "\(lround(Double(cutTotalTime)))s"
  167. }
  168. /// 更新进度
  169. /// progress <#progress description#>
  170. func updateProgress(progress: CGFloat) {
  171. if currentPanView != nil {
  172. return
  173. }
  174. let width = rightOprateView.frame.minX - leftOprateView.frame.maxX
  175. var newX = leftOprateView.frame.maxX - ((progressView.frame.width - 3) / 2) + progress * width
  176. BFLog(message: "progress = \(progress),newX = \(newX)")
  177. if newX.isNaN || newX <= (leftOprateView.frame.maxX - ((progressView.frame.width - 3) / 2)) {
  178. newX = (leftOprateView.frame.maxX - ((progressView.frame.width - 3) / 2))
  179. }
  180. if newX >= (rightOprateView.frame.minX - (((progressView.frame.width - 3) / 2) + 3)) {
  181. newX = (rightOprateView.frame.minX - (((progressView.frame.width - 3) / 2) + 3))
  182. }
  183. progressView.frame.origin.x = newX
  184. }
  185. deinit {
  186. BFLog(message: "卡点裁剪-裁剪时长视图销毁")
  187. }
  188. }
  189. extension PQVideoCutingOprateView {
  190. /// 操作手势
  191. /// - Parameter ges: <#ges description#>
  192. /// - Returns: <#description#>
  193. @objc func panGesture(gesture: UIPanGestureRecognizer) {
  194. switch gesture.state {
  195. case .began:
  196. preOriginX = 0
  197. currentPanView = gesture.view
  198. case .changed:
  199. if currentPanView == leftOprateView || currentPanView == rightOprateView || currentPanView == progressView {
  200. let point = gesture.translation(in: superview)
  201. var offsetX = point.x - preOriginX
  202. preOriginX = point.x
  203. if currentPanView == leftOprateView {
  204. var oprateFrame = leftOprateView.frame
  205. oprateFrame.origin.x = oprateFrame.origin.x + offsetX
  206. if oprateFrame.origin.x <= leftMargin {
  207. offsetX += leftMargin - oprateFrame.origin.x
  208. oprateFrame.origin.x = leftMargin
  209. }
  210. let minLength = rightOprateView.frame.minX - cutingOprateWidth - cutMinDuration * perSecondWidth
  211. if oprateFrame.origin.x >= minLength {
  212. offsetX -= oprateFrame.origin.x - minLength
  213. oprateFrame.origin.x = minLength
  214. }
  215. let maxLength = rightOprateView.frame.minX - cutingOprateWidth - cutMaxDuration * perSecondWidth
  216. if oprateFrame.origin.x <= maxLength {
  217. offsetX += maxLength - oprateFrame.origin.x
  218. oprateFrame.origin.x = maxLength
  219. }
  220. let time = offsetX / perSecondWidth
  221. cutStartTime = cutStartTime + time
  222. leftOprateView.frame = oprateFrame
  223. } else if currentPanView == rightOprateView {
  224. var oprateFrame = rightOprateView.frame
  225. oprateFrame.origin.x += offsetX
  226. var rightImageMaxX = leftOprateView.frame.maxX + cutMaxDuration * perSecondWidth
  227. if rightImageMaxX > frame.width - rightMargin - cutingOprateWidth {
  228. rightImageMaxX = frame.width - rightMargin - cutingOprateWidth
  229. }
  230. if oprateFrame.origin.x >= rightImageMaxX {
  231. offsetX -= oprateFrame.origin.x - rightImageMaxX
  232. oprateFrame.origin.x = rightImageMaxX
  233. }
  234. let rightImageMinX = leftOprateView.frame.maxX + cutMinDuration * perSecondWidth
  235. if oprateFrame.origin.x <= rightImageMinX {
  236. offsetX += rightImageMinX - oprateFrame.origin.x
  237. oprateFrame.origin.x = rightImageMinX
  238. }
  239. let time = offsetX / perSecondWidth
  240. cutEndTime = cutEndTime + time
  241. rightOprateView.frame = oprateFrame
  242. } else if currentPanView == progressView {
  243. var progressFrame = progressView.frame
  244. BFLog(message: "progressFrame = \(progressFrame),offsetX = \(offsetX)")
  245. progressFrame.origin.x += offsetX
  246. if progressFrame.origin.x <= (leftOprateView.frame.maxX - ((progressView.frame.width - 3) / 2)) {
  247. progressFrame.origin.x = (leftOprateView.frame.maxX - ((progressView.frame.width - 3) / 2))
  248. }
  249. if progressFrame.origin.x >= (rightOprateView.frame.minX - (((progressView.frame.width - 3) / 2) + 3)) {
  250. progressFrame.origin.x = (rightOprateView.frame.minX - (((progressView.frame.width - 3) / 2) + 3))
  251. }
  252. progressView.frame = progressFrame
  253. BFLog(message: "======\(progressView.frame.minX - (leftOprateView.frame.maxX - ((progressView.frame.width - 3) / 2)))")
  254. if progressDidChanged != nil {
  255. progressDidChanged!(progress)
  256. }
  257. }
  258. if currentPanView != progressView {
  259. cutTotalTime = cutEndTime - cutStartTime
  260. // 更新子视图布局
  261. updateSubViewFrame()
  262. if cutRangeDidChanged != nil {
  263. BFLog(message: "cutStartTime = \(cutStartTime),cutEndTime = \(cutEndTime)")
  264. cutRangeDidChanged!(cutStartTime, cutEndTime, cutTotalTime)
  265. }
  266. }
  267. }
  268. case .ended:
  269. if currentPanView == leftOprateView || currentPanView == rightOprateView || currentPanView == progressView {
  270. if didEndDragging != nil {
  271. didEndDragging!(currentPanView == leftOprateView ? 1 : (currentPanView == rightOprateView ? 2 : 3), cutStartTime, cutEndTime, progress)
  272. }
  273. }
  274. currentPanView = nil
  275. default:
  276. break
  277. }
  278. }
  279. }