BFVideoThumbProgressView.swift 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. //
  2. // BFVideoThumbProgressView.swift
  3. // BFRecordScreenKit
  4. //
  5. // Created by 胡志强 on 2021/12/3.
  6. //
  7. import AVFoundation
  8. import BFCommonKit
  9. import BFUIKit
  10. import Foundation
  11. import SnapKit
  12. import UIKit
  13. class BFVideoThumbProgressView: UIView {
  14. var recordItem: BFRecordItemModel? {
  15. didSet {
  16. // 指针回归
  17. progress = 0
  18. if recordItem?.mediaType == .VIDEO {
  19. dealWithVideoThumb()
  20. } else if recordItem?.mediaType == .IMAGE {
  21. dealWithImageThumb()
  22. }
  23. }
  24. }
  25. var dragScrollProgressHandle: ((Bool, Float) -> Void)?
  26. var dragEndHandle: ((Float) -> Void)?
  27. var dragStartHandle: (() -> Void)?
  28. var isDrag = false
  29. let thumbImageWidth : CGFloat = 70.0
  30. let fetchThumbStrategy: BFVideoThumbProgressStrategyProtocol = BFVideoThumbProgressStrategy()
  31. var progress: Double = 0 {
  32. didSet {
  33. updateProgress(progress: progress)
  34. }
  35. }
  36. var thumbImgs = [UIImage]()
  37. lazy var progressView: BFAutolayoutScrollView = {
  38. let sv = BFAutolayoutScrollView()
  39. sv.bounces = false
  40. // sv.backgroundColor = .clear
  41. // sv.backgroundColor = UIColor.hexColor(hexadecimal: "#888888",alpha:0.3)
  42. sv.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.3)
  43. sv.decelerationRate = .fast
  44. sv.showsHorizontalScrollIndicator = false
  45. sv.delegate = self
  46. return sv
  47. }()
  48. lazy var progessIndicateBackV: UIView = {
  49. let vv = UIView()
  50. return vv
  51. }()
  52. // MARK: - 生命周期
  53. override init(frame: CGRect) {
  54. super.init(frame: frame)
  55. addSubview(progressView)
  56. let line = UIView()
  57. line.backgroundColor = .white
  58. line.layer.borderColor = UIColor.black.cgColor
  59. line.layer.borderWidth = 0.3
  60. line.layer.cornerRadius = 1.5
  61. addSubview(line)
  62. line.snp.makeConstraints { make in
  63. make.width.equalTo(3)
  64. make.center.height.equalToSuperview()
  65. }
  66. progressView.contentView.addSubview(progessIndicateBackV)
  67. progessIndicateBackV.snp.remakeConstraints { make in
  68. make.left.equalTo(width * 0.5)
  69. make.right.equalTo(width * -0.5)
  70. make.bottom.equalToSuperview()
  71. make.height.equalTo(6)
  72. }
  73. }
  74. required init?(coder _: NSCoder) {
  75. fatalError("init(coder:) has not been implemented")
  76. }
  77. override func layoutSubviews() {
  78. super.layoutSubviews()
  79. progressView.snp.makeConstraints { make in
  80. make.edges.equalToSuperview()
  81. }
  82. }
  83. /// 处理视频缩略图
  84. func dealWithVideoThumb() {
  85. guard let videoAsset = recordItem?.videoAsset else {
  86. return
  87. }
  88. self.addThumbImages(images: recordItem!.thumbImgs)
  89. if recordItem!.thumbImgs.count > 0{
  90. return
  91. }
  92. let date = Date()
  93. let dur = videoAsset.duration.seconds
  94. if dur > 0 {
  95. let count = fetchThumbStrategy.frameNumberOfVideo(assetDuration: dur)
  96. let fps = Double(count) / dur
  97. splitVideoFileUrlFps(urlAsset: videoAsset, fps: fps, firstImagesCount: Int(ceil(width/2.0/thumbImageWidth))) { [weak self] hadGetAll, images in
  98. BFLog(1, message: "获取缩略图:\(hadGetAll), \(Date().timeIntervalSince(date))")
  99. self?.recordItem!.thumbImgs.removeAll()
  100. self?.recordItem!.thumbImgs.append(contentsOf: images)
  101. self?.addThumbImages(images: images)
  102. }
  103. }
  104. }
  105. /// 处理图片缩略图
  106. func dealWithImageThumb() {
  107. guard let image = recordItem?.coverImg else {
  108. return
  109. }
  110. addThumbImages(images: Array(repeating: image, count: 10))
  111. }
  112. /// 添加缩略图
  113. /// - Parameter images: <#images description#>
  114. func addThumbImages(images: [UIImage]) {
  115. DispatchQueue.main.async { [weak self] in
  116. self?.progressView.contentView.subviews.forEach { subview in
  117. if subview is UIImageView {
  118. subview.removeFromSuperview()
  119. }
  120. }
  121. if images.count > 0 {
  122. self?.thumbImgs = images
  123. if let sself = self {
  124. var lastiv = UIImageView()
  125. for (i, img) in images.enumerated() {
  126. let iv = UIImageView(image: img)
  127. iv.contentMode = .scaleAspectFill
  128. iv.clipsToBounds = true
  129. sself.progressView.contentView.insertSubview(iv, belowSubview: sself.progessIndicateBackV)
  130. iv.snp.makeConstraints { make in
  131. make.left.equalTo(CGFloat(i) * CGFloat(sself.thumbImageWidth) + sself.width * 0.5)
  132. make.top.bottom.equalToSuperview()
  133. make.height.equalTo(50)
  134. make.width.equalTo(sself.thumbImageWidth)
  135. }
  136. lastiv = iv
  137. }
  138. lastiv.snp.makeConstraints { make in
  139. make.right.equalTo(sself.width * -0.5)
  140. }
  141. // sself.progressView.contentView.addSubview(sself.progessIndicateBackV)
  142. // sself.progessIndicateBackV.snp.remakeConstraints { make in
  143. // make.left.equalTo(sself.width * 0.5)
  144. // make.right.equalTo(sself.width * -0.5)
  145. // make.bottom.equalToSuperview()
  146. // make.height.equalTo(6)
  147. // }
  148. }
  149. }
  150. }
  151. }
  152. func appendThumb(progress: Double = 0) {
  153. let count: Int = Int(progress / 2)
  154. BFLog(message: "需要的图片个数:progress=\(progress),count=\(count)")
  155. if recordItem?.mediaType == .IMAGE, (thumbImgs.count - 5) < count {
  156. guard let image = recordItem?.coverImg else {
  157. return
  158. }
  159. var lastiv = UIImageView()
  160. let lastIndex = thumbImgs.count - 1
  161. for i in lastIndex ... lastIndex + 10 {
  162. let iv = UIImageView(image: image)
  163. iv.contentMode = .scaleAspectFill
  164. iv.clipsToBounds = true
  165. progressView.contentView.addSubview(iv)
  166. iv.snp.makeConstraints { make in
  167. make.left.equalTo(CGFloat(i) * CGFloat(thumbImageWidth) + width * 0.5)
  168. make.top.bottom.equalToSuperview()
  169. make.height.equalTo(50)
  170. make.width.equalTo(thumbImageWidth)
  171. }
  172. lastiv = iv
  173. thumbImgs.append(image)
  174. }
  175. lastiv.snp.makeConstraints { make in
  176. make.right.equalTo(width * -0.5)
  177. }
  178. }
  179. }
  180. /// 更新进度
  181. /// - Parameter progress: <#progress description#>
  182. func updateProgress(progress: Double = 0) {
  183. if progressView.contentSize.width <= 0 {
  184. return
  185. }
  186. if recordItem?.mediaType == .VIDEO {
  187. if let second = recordItem?.videoAsset?.duration.seconds, second > 0 {
  188. let w = progressView.contentSize.width - width
  189. BFLog(message: "录音进度--指示器:progress=\(progress),duration=\(second),w=\(w),perW=\(Double(w) / second),totalW:\(progress * Double(w) / second)")
  190. progressView.contentOffset = CGPoint(x: progress * Double(w) / second, y: 0)
  191. }
  192. } else if recordItem?.mediaType == .IMAGE {
  193. // if (recordItem?.materialDuraion ?? 0) > progress {
  194. BFLog(message: "updateProgress:\(progress)")
  195. progressView.contentOffset = CGPoint(x: progress * thumbImageWidth / 2.0, y: 0)
  196. appendThumb(progress: progress)
  197. // }
  198. }
  199. }
  200. }
  201. extension BFVideoThumbProgressView: UIScrollViewDelegate {
  202. func scrollViewDidScroll(_ scrollView: UIScrollView) {
  203. if recordItem?.mediaType == .VIDEO {
  204. if isDrag {
  205. let dur = scrollView.contentOffset.x / (scrollView.contentSize.width - width)
  206. dragScrollProgressHandle?(false, Float(dur))
  207. }
  208. } else if recordItem?.mediaType == .IMAGE {
  209. if isDrag {
  210. if scrollView.contentOffset.x > CGFloat(recordItem?.materialDuraion ?? 0) * thumbImageWidth / 2.0 {
  211. scrollView.contentOffset = CGPoint(x: CGFloat(recordItem?.materialDuraion ?? 0) * thumbImageWidth / 2.0, y: 0)
  212. }
  213. let dur = scrollView.contentOffset.x / (CGFloat(recordItem?.materialDuraion ?? 0) * thumbImageWidth / 2.0)
  214. dragScrollProgressHandle?(false, Float(dur))
  215. }
  216. }
  217. }
  218. func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
  219. isDrag = true
  220. var dur: CGFloat = 0
  221. if recordItem?.mediaType == .VIDEO {
  222. dur = scrollView.contentOffset.x / (scrollView.contentSize.width - width)
  223. } else {
  224. dur = scrollView.contentOffset.x / (CGFloat(recordItem?.materialDuraion ?? 0) * thumbImageWidth / 2.0)
  225. }
  226. dragStartHandle?()
  227. dragScrollProgressHandle?(true, Float(dur))
  228. }
  229. func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
  230. if !decelerate {
  231. var dur: CGFloat = 0
  232. if recordItem?.mediaType == .VIDEO {
  233. dur = scrollView.contentOffset.x / (scrollView.contentSize.width - width)
  234. } else {
  235. dur = scrollView.contentOffset.x / (CGFloat(recordItem?.materialDuraion ?? 0) * thumbImageWidth / 2.0)
  236. }
  237. isDrag = false
  238. dragEndHandle?(Float(dur))
  239. }
  240. }
  241. func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
  242. var dur: CGFloat = 0
  243. if recordItem?.mediaType == .VIDEO {
  244. dur = scrollView.contentOffset.x / (scrollView.contentSize.width - width)
  245. } else {
  246. dur = scrollView.contentOffset.x / (CGFloat(recordItem?.materialDuraion ?? 0) * thumbImageWidth / 2.0)
  247. }
  248. isDrag = false
  249. dragEndHandle?(Float(dur))
  250. }
  251. }