123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 |
- //
- // 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 {
- // 指针回归
- BFLog(1, message: "new recorditem")
- 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: CGFloat = 70.0
- let fetchThumbStrategy: BFVideoThumbProgressStrategyProtocol = BFVideoThumbProgressStrategy()
- var progress: Double = 0 { // 进度,秒为单位
- didSet {
- updateProgress(progress: progress)
- }
- }
- var thumbImgs = [UIImage]()
- var lastImg: UIImageView?
- 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()
- }
- progressView.contentView.addSubview(progessIndicateBackV)
- progessIndicateBackV.snp.makeConstraints { make in
- make.left.equalTo(width * 0.5)
- make.right.equalTo(width * -0.5)
- make.bottom.equalToSuperview()
- make.height.equalTo(6)
- make.width.equalTo(1).priority(.high)
- }
- }
- 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 {
- // 可能icloud资源没回来,清空原有内容
- addThumbImages(images: [UIImage]())
- return
- }
- addThumbImages(images: recordItem!.thumbImgs)
- if recordItem!.thumbImgs.count > 0 {
- // 代表已经获取过了,不用在重新去获得
- progessIndicateBackV.snp.remakeConstraints { make in
- make.left.equalTo(width * 0.5)
- make.right.equalTo(width * -0.5)
- make.bottom.equalToSuperview()
- make.height.equalTo(6)
- make.width.equalTo(CGFloat(recordItem!.thumbImgs.count) * thumbImageWidth).priority(.high)
- }
- return
- }
- let date = Date()
- let dur = videoAsset.duration.seconds
- if dur > 0 {
- let count = fetchThumbStrategy.frameNumberOfVideo(assetDuration: dur)
- progessIndicateBackV.snp.remakeConstraints { make in
- make.left.equalTo(width * 0.5)
- make.right.equalTo(width * -0.5)
- make.bottom.equalToSuperview()
- make.height.equalTo(6)
- make.width.equalTo(CGFloat(count) * thumbImageWidth).priority(.high)
- }
- recordItem!.splitVideoFileUrlFps(frames: count, firstImagesCount: Int(ceil(width / 2.0 / thumbImageWidth))) { [weak self, weak recordItem] hadGetAll, images in
- guard let sself = self, let sitem = recordItem else {
- return
- }
- BFLog(1, message: "获取缩略图:\(hadGetAll), \(Date().timeIntervalSince(date)), \(sitem.localPath ?? "aa")")
- sitem.thumbImgs.removeAll()
- sitem.thumbImgs.append(contentsOf: images)
- // 不足数则补充足够帧数
- while hadGetAll, sitem.thumbImgs.count < count, images.count > 0 {
- sitem.thumbImgs.append(images.last!)
- }
- if sitem.localPath == sself.recordItem!.localPath {
- sself.addThumbImages(images: sitem.thumbImgs)
- } else {
- BFLog(1, message: "thumbImgs.count:\(sitem.thumbImgs.count)")
- }
- }
- }
- }
- /// 处理图片缩略图
- func dealWithImageThumb() {
- guard let image = (recordItem?.thumbImgs.first ?? recordItem?.coverImg) else {
- addThumbImages(images: [UIImage]())
- return
- }
- addThumbImages(images: Array(repeating: image, count: 10))
- }
- /// 添加缩略图
- /// - Parameter images: <#images description#>
- func addThumbImages(images: [UIImage]) {
- DispatchQueue.main.async { [weak self] in
- self?.progressView.contentView.subviews.forEach { subview in
- if subview is UIImageView {
- subview.removeFromSuperview()
- }
- }
- if images.count > 0 {
- if let sself = self {
- if sself.lastImg != nil, sself.lastImg?.superview != nil {
- sself.lastImg?.removeFromSuperview()
- }
- for (i, img) in images.enumerated() {
- let iv = UIImageView(image: img)
- iv.contentMode = .scaleAspectFill
- iv.clipsToBounds = true
- sself.progressView.contentView.insertSubview(iv, belowSubview: sself.progessIndicateBackV)
- 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)
- }
- sself.lastImg = iv
- }
- if sself.recordItem?.mediaType == .IMAGE {
- // 图片需要动态修改宽度
- sself.lastImg?.snp.makeConstraints { make in
- make.right.equalTo(sself.width * -0.5)
- }
- }
- }
- }
- }
- }
- func appendThumb(progress: Double = 0) {
- DispatchQueue.main.async { [weak self] in
- guard let sslf = self else { return }
- let count: Int = Int(progress / 2)
- // BFLog(message: "需要的图片个数:progress=\(progress),count=\(count)")
- if sslf.recordItem?.mediaType == .IMAGE, (sslf.progressView.contentView.subviews.count - 6) < count {
- guard let image = (sslf.recordItem?.thumbImgs.first ?? sslf.recordItem?.coverImg) else {
- return
- }
- if sslf.lastImg != nil, sslf.lastImg?.superview != nil {
- sslf.lastImg?.removeFromSuperview()
- }
- let lastIndex = sslf.progressView.contentView.subviews.count - 1
- for i in lastIndex ... lastIndex + 10 {
- let iv = UIImageView(image: image)
- iv.contentMode = .scaleAspectFill
- iv.clipsToBounds = true
- sslf.progressView.contentView.addSubview(iv)
- iv.snp.makeConstraints { make in
- make.left.equalTo(CGFloat(i) * CGFloat(sslf.thumbImageWidth) + sslf.width * 0.5)
- make.top.bottom.equalToSuperview()
- make.height.equalTo(50)
- make.width.equalTo(sslf.thumbImageWidth)
- }
- sslf.lastImg = iv
- }
- sslf.lastImg?.snp.makeConstraints { make in
- make.right.equalTo(sslf.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?.materialDuraion.seconds, second > 0 {
- let w = progressView.contentSize.width - width
- BFLog(message: "录音进度--指示器:progress=\(progress),duration=\(second),w=\(w),perW=\(Double(w) / second),totalW:\(progress * Double(w) / second)")
- 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) {
- let totalW = recordItem?.mediaType == .VIDEO ? (scrollView.contentSize.width - width) : (CGFloat(recordItem?.materialDuraion.seconds ?? 0) * thumbImageWidth / 2.0)
- if recordItem?.mediaType == .VIDEO {
- if isDrag {
- dragScrollProgressHandle?(false, totalW > 0 ? Float(scrollView.contentOffset.x / totalW) : 0)
- }
- } else if recordItem?.mediaType == .IMAGE {
- if isDrag {
- if scrollView.contentOffset.x > ((CGFloat(recordItem?.materialDuraion.seconds ?? 0) * thumbImageWidth / 2.0) + 0.34) {
- scrollView.contentOffset = CGPoint(x: (CGFloat(recordItem?.materialDuraion.seconds ?? 0) * thumbImageWidth / 2.0) + 0.34, y: 0)
- }
- dragScrollProgressHandle?(false, totalW > 0 ? Float(scrollView.contentOffset.x / totalW) : 0)
- }
- }
- }
- func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
- isDrag = true
- let totalW = recordItem?.mediaType == .VIDEO ? (scrollView.contentSize.width - width) : (CGFloat(recordItem?.materialDuraion.seconds ?? 0) * thumbImageWidth / 2.0)
- dragStartHandle?()
- dragScrollProgressHandle?(true, totalW > 0 ? Float(scrollView.contentOffset.x / totalW) : 0)
- }
- func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
- if !decelerate {
- let totalW = recordItem?.mediaType == .VIDEO ? (scrollView.contentSize.width - width) : (CGFloat(recordItem?.materialDuraion.seconds ?? 0) * thumbImageWidth / 2.0)
- isDrag = false
- dragEndHandle?(totalW > 0 ? Float(scrollView.contentOffset.x / totalW) : 0)
- }
- }
- func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
- let totalW = recordItem?.mediaType == .VIDEO ? (scrollView.contentSize.width - width) : (CGFloat(recordItem?.materialDuraion.seconds ?? 0) * thumbImageWidth / 2.0)
- isDrag = false
- dragEndHandle?(totalW > 0 ? min(Float(scrollView.contentOffset.x / totalW), 1) : 0)
- }
- }
|