// // BFVideoThumbProgressView.swift // BFRecordScreenKit // // Created by 胡志强 on 2021/12/3. // import AVFoundation import BFCommonKit import BFUIKit import Foundation import SnapKit import UIKit class BFVideoThumbProgressView: UIView { var recordItem: BFRecordItemModel? { didSet { // 指针回归 progress = 0 if recordItem?.mediaType == .VIDEO { dealWithVideoThumb() } else if recordItem?.mediaType == .IMAGE { dealWithImageThumb() } } } var dragScrollProgressHandle: ((Bool, Float) -> Void)? var dragEndHandle: ((Float) -> Void)? var dragStartHandle: (() -> Void)? var isDrag = false let thumbImageWidth = 70.0 let fetchThumbStrategy: BFVideoThumbProgressStrategyProtocol = BFVideoThumbProgressStrategy() var progress: Double = 0 { didSet { updateProgress(progress: progress) } } var thumbImgs = [UIImage]() lazy var progressView: BFAutolayoutScrollView = { let sv = BFAutolayoutScrollView() sv.bounces = false // sv.backgroundColor = .clear // sv.backgroundColor = UIColor.hexColor(hexadecimal: "#888888",alpha:0.3) sv.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.3) sv.decelerationRate = .fast sv.showsHorizontalScrollIndicator = false sv.delegate = self return sv }() lazy var progessIndicateBackV: UIView = { let vv = UIView() return vv }() // MARK: - 生命周期 override init(frame: CGRect) { super.init(frame: frame) addSubview(progressView) let line = UIView() line.backgroundColor = .white line.layer.borderColor = UIColor.black.cgColor line.layer.borderWidth = 0.3 line.layer.cornerRadius = 1.5 addSubview(line) line.snp.makeConstraints { make in make.width.equalTo(3) make.center.height.equalToSuperview() } } required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } override func layoutSubviews() { super.layoutSubviews() progressView.snp.makeConstraints { make in make.edges.equalToSuperview() } } /// 处理视频缩略图 func dealWithVideoThumb() { guard let videoAsset = recordItem?.videoAsset else { return } let dur = videoAsset.duration.seconds if dur > 0 { let fps = Double(fetchThumbStrategy.frameNumberOfVideo(assetDuration: dur)) / dur splitVideoFileUrlFps(urlAsset: videoAsset, fps: fps) { [weak self] images in self?.addThumbImages(images: images) } } } /// 处理图片缩略图 func dealWithImageThumb() { guard let image = recordItem?.coverImg else { return } addThumbImages(images: Array(repeating: image, count: 10)) } /// 添加缩略图 /// - Parameter images: <#images description#> func addThumbImages(images: [UIImage]) { if images.count > 0 { thumbImgs = images DispatchQueue.main.async { [weak self] in self?.progressView.contentView.subviews.forEach { subview in subview.removeFromSuperview() } if let sself = self { var lastiv = UIImageView() for (i, img) in images.enumerated() { let iv = UIImageView(image: img) iv.contentMode = .scaleAspectFill iv.clipsToBounds = true sself.progressView.contentView.addSubview(iv) iv.snp.makeConstraints { make in make.left.equalTo(CGFloat(i) * CGFloat(sself.thumbImageWidth) + sself.width * 0.5) make.top.bottom.equalToSuperview() make.height.equalTo(50) make.width.equalTo(sself.thumbImageWidth) } lastiv = iv } lastiv.snp.makeConstraints { make in make.right.equalTo(sself.width * -0.5) } sself.progressView.contentView.addSubview(sself.progessIndicateBackV) sself.progessIndicateBackV.snp.makeConstraints { make in make.left.equalTo(sself.width * 0.5) make.right.equalTo(sself.width * -0.5) make.bottom.equalToSuperview() make.height.equalTo(6) } } } } } func appendThumb(progress: Double = 0) { let count: Int = Int(progress / 2) if recordItem?.mediaType == .IMAGE, thumbImgs.count < (count - 5) { guard let image = recordItem?.coverImg else { return } var lastiv = UIImageView() let lastIndex = thumbImgs.count - 1 for i in lastIndex ... lastIndex + 10 { let iv = UIImageView(image: image) iv.contentMode = .scaleAspectFill iv.clipsToBounds = true progressView.contentView.addSubview(iv) iv.snp.makeConstraints { make in make.left.equalTo(CGFloat(i) * CGFloat(thumbImageWidth) + width * 0.5) make.top.bottom.equalToSuperview() make.height.equalTo(50) make.width.equalTo(thumbImageWidth) } lastiv = iv thumbImgs.append(image) } lastiv.snp.makeConstraints { make in make.right.equalTo(width * -0.5) } } } /// 更新进度 /// - Parameter progress: <#progress description#> func updateProgress(progress: Double = 0) { if progressView.contentSize.width <= 0 { return } if recordItem?.mediaType == .VIDEO { if let second = recordItem?.videoAsset?.duration.seconds, second > 0 { let w = progressView.contentSize.width - width progressView.contentOffset = CGPoint(x: progress * Double(w) / second, y: 0) } } else if recordItem?.mediaType == .IMAGE { // if (recordItem?.materialDuraion ?? 0) > progress { BFLog(message: "updateProgress:\(progress)") progressView.contentOffset = CGPoint(x: progress * thumbImageWidth / 2.0, y: 0) appendThumb(progress: progress) // } } } } extension BFVideoThumbProgressView: UIScrollViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) { if recordItem?.mediaType == .VIDEO { if isDrag { let dur = scrollView.contentOffset.x / (scrollView.contentSize.width - width) dragScrollProgressHandle?(false, Float(dur)) } } else if recordItem?.mediaType == .IMAGE { if scrollView.contentOffset.x > CGFloat(recordItem?.materialDuraion ?? 0) * thumbImageWidth / 2.0 { scrollView.contentOffset = CGPoint(x: CGFloat(recordItem?.materialDuraion ?? 0) * thumbImageWidth / 2.0, y: 0) } if isDrag { let dur = scrollView.contentOffset.x / (CGFloat(recordItem?.materialDuraion ?? 0) * thumbImageWidth / 2.0) dragScrollProgressHandle?(false, Float(dur)) } } } func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { isDrag = true var dur: CGFloat = 0 if recordItem?.mediaType == .VIDEO { dur = scrollView.contentOffset.x / (scrollView.contentSize.width - width) } else { dur = scrollView.contentOffset.x / (CGFloat(recordItem?.materialDuraion ?? 0) * thumbImageWidth / 2.0) } dragStartHandle?() dragScrollProgressHandle?(true, Float(dur)) } func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { if !decelerate { var dur: CGFloat = 0 if recordItem?.mediaType == .VIDEO { dur = scrollView.contentOffset.x / (scrollView.contentSize.width - width) } else { dur = scrollView.contentOffset.x / (CGFloat(recordItem?.materialDuraion ?? 0) * thumbImageWidth / 2.0) } isDrag = false dragEndHandle?(Float(dur)) } } func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { var dur: CGFloat = 0 if recordItem?.mediaType == .VIDEO { dur = scrollView.contentOffset.x / (scrollView.contentSize.width - width) } else { dur = scrollView.contentOffset.x / (CGFloat(recordItem?.materialDuraion ?? 0) * thumbImageWidth / 2.0) } isDrag = false dragEndHandle?(Float(dur)) } }