// // PQVideoCutingOprateView.swift // PQSpeed // // Created by SanW on 2021/5/9. // Copyright © 2021 BytesFlow. All rights reserved. // import UIKit import BFCommonKit class PQVideoCutingOprateView: UIView { // 距离左边间隔 var leftMargin: CGFloat = 0 // 距离右边间隔 var rightMargin: CGFloat = 0 // 距离上边间隔 var topMargin: CGFloat = 14 // 距离下边间隔 var bottomMargin: CGFloat = 20 /// 上下线条的高度 var lineHeight: CGFloat = 3.0 /// 进度宽度 var progressWidth: CGFloat = 12.0 // 左右裁剪操作宽度 var cutingOprateWidth: CGFloat = 15 // 开始时间 默认 0 private var cutStartTime: CGFloat = 0 // 结束时间 private var cutEndTime: CGFloat = 0 // 视频总时长 private var totalDuration: CGFloat = 0 // 最小裁剪大小 默认 10s private var cutMinDuration: CGFloat = 10 // 最大裁剪大小 默认 40s private var cutMaxDuration: CGFloat = 40 /// 每秒宽度 private var perSecondWidth: CGFloat = 0 /// 裁剪总时长 private var cutTotalTime: CGFloat = 0 /// 当前滑动的位置 private var preOriginX: CGFloat = 0 /// 当前滑动的view private var currentPanView: UIView? private var progress: CGFloat { if progressView.frame.minX >= (rightOprateView.frame.minX - (((progressView.frame.width - 3) / 2) + 3)) { return 1 } else { return ((progressView.frame.minX - (leftOprateView.frame.maxX - ((progressView.frame.width - 3) / 2))) / perSecondWidth) / (cutEndTime - cutStartTime) } } /// 拖缀结束的回调 type - 1-拖动左边裁剪结束 2--拖动右边裁剪结束 3-进度条拖动结束 4-滑动结束 var didEndDragging: ((_ type: Int, _ startTime: CGFloat, _ endTime: CGFloat, _ progress: CGFloat) -> Void)? /// 裁剪实时回调 var cutRangeDidChanged: ((_ startTime: CGFloat, _ endTime: CGFloat, _ cutTotalTime: CGFloat) -> Void)? /// 进度回调 var progressDidChanged: ((_ progress: CGFloat) -> Void)? lazy var durationLabel: UILabel = { let durationLabel = UILabel() durationLabel.font = UIFont.systemFont(ofSize: 16, weight: .medium) durationLabel.backgroundColor = PQBFConfig.shared.cutDurationColor durationLabel.textColor = UIColor.white durationLabel.textAlignment = .center durationLabel.addShadow() return durationLabel }() lazy var leftOprateView: UIImageView = { let leftOprateView = UIImageView(image:UIImage.moduleImage(named: "videomk_crop_left", moduleName: "BFFramework",isAssets: false)?.withRenderingMode(.alwaysTemplate)) leftOprateView.tintColor = PQBFConfig.shared.cutViewTintColor leftOprateView.contentMode = .scaleAspectFill leftOprateView.isUserInteractionEnabled = true leftOprateView.backgroundColor = PQBFConfig.shared.cutViewStyleColor let panGes = UIPanGestureRecognizer(target: self, action: #selector(panGesture(gesture:))) panGes.maximumNumberOfTouches = 1 panGes.minimumNumberOfTouches = 1 leftOprateView.addGestureRecognizer(panGes) return leftOprateView }() lazy var rightOprateView: UIImageView = { let rightOprateView = UIImageView(image:UIImage.moduleImage(named: "videomk_crop_right", moduleName: "BFFramework",isAssets: false)?.withRenderingMode(.alwaysTemplate)) rightOprateView.tintColor = PQBFConfig.shared.cutViewTintColor rightOprateView.contentMode = .scaleAspectFill rightOprateView.isUserInteractionEnabled = true rightOprateView.backgroundColor = PQBFConfig.shared.cutViewStyleColor let panGes = UIPanGestureRecognizer(target: self, action: #selector(panGesture(gesture:))) panGes.maximumNumberOfTouches = 1 panGes.minimumNumberOfTouches = 1 rightOprateView.addGestureRecognizer(panGes) return rightOprateView }() lazy var topLineView: UIImageView = { let topLineView = UIImageView() topLineView.backgroundColor = PQBFConfig.shared.cutViewStyleColor return topLineView }() lazy var bottomLineView: UIImageView = { let bottomLineView = UIImageView() bottomLineView.backgroundColor = PQBFConfig.shared.cutViewStyleColor return bottomLineView }() lazy var progressView: PQCuttingPointView = { let progressView = PQCuttingPointView(frame: CGRect(x: 0, y: 0, width: progressWidth, height: frame.height)) let panGes = UIPanGestureRecognizer(target: self, action: #selector(panGesture(gesture:))) panGes.maximumNumberOfTouches = 1 panGes.minimumNumberOfTouches = 1 progressView.addGestureRecognizer(panGes) return progressView }() override private init(frame: CGRect) { super.init(frame: frame) } required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } init(frame: CGRect, duration: CGFloat, startTime: CGFloat, endTime: CGFloat, minDuration: CGFloat, maxDuration: CGFloat) { super.init(frame: frame) isUserInteractionEnabled = true clipsToBounds = true backgroundColor = UIColor.clear totalDuration = duration cutStartTime = startTime cutEndTime = endTime cutMinDuration = minDuration cutMaxDuration = maxDuration if cutMaxDuration <= 0 || cutMaxDuration > totalDuration { cutMaxDuration = totalDuration } if cutMinDuration <= 0 || (cutMinDuration > totalDuration) { cutMinDuration = 7 } if cutEndTime <= 0 { cutEndTime = cutStartTime + cutMinDuration } perSecondWidth = 7 cutTotalTime = cutEndTime - cutStartTime configSubview() BFLog(message: "======\(frame),perSecondWidth = \(perSecondWidth),cutMaxDuration = \(cutMaxDuration * perSecondWidth)") } /// 初始化视图 /// - Returns: <#description#> func configSubview() { if leftOprateView.superview == nil { addSubview(leftOprateView) } if rightOprateView.superview == nil { addSubview(rightOprateView) } if topLineView.superview == nil { addSubview(topLineView) } if bottomLineView.superview == nil { addSubview(bottomLineView) } if durationLabel.superview == nil { addSubview(durationLabel) } if progressView.superview == nil { addSubview(progressView) } leftOprateView.frame = CGRect(x: leftMargin + perSecondWidth * cutStartTime, y: topMargin, width: cutingOprateWidth, height: frame.height - topMargin - bottomMargin) rightOprateView.frame = CGRect(x: leftOprateView.frame.maxX + perSecondWidth * (cutEndTime - cutStartTime), y: leftOprateView.frame.minY, width: cutingOprateWidth, height: frame.height - topMargin - bottomMargin) // 更新子视图布局 updateSubViewFrame() } /// 更新子视图布局 /// - Returns: <#description#> func updateSubViewFrame() { topLineView.frame = CGRect(x: leftOprateView.frame.maxX, y: leftOprateView.frame.minY, width: rightOprateView.frame.minX - leftOprateView.frame.maxX, height: lineHeight) bottomLineView.frame = CGRect(x: topLineView.frame.minX, y: leftOprateView.frame.maxY - lineHeight, width: topLineView.frame.width, height: lineHeight) progressView.frame = CGRect(x: leftOprateView.frame.maxX - ((progressView.frame.width - 3) / 2), y: 0, width: progressWidth, height: frame.height) durationLabel.frame = CGRect(x: topLineView.frame.minX, y: topLineView.frame.maxY, width: topLineView.frame.width, height: bottomLineView.frame.minY - topLineView.frame.maxY) durationLabel.text = "\(lround(Double(cutTotalTime)))s" } /// 更新进度 /// progress <#progress description#> func updateProgress(progress: CGFloat) { if currentPanView != nil { return } let width = rightOprateView.frame.minX - leftOprateView.frame.maxX var newX = leftOprateView.frame.maxX - ((progressView.frame.width - 3) / 2) + progress * width BFLog(message: "progress = \(progress),newX = \(newX)") if newX.isNaN || newX <= (leftOprateView.frame.maxX - ((progressView.frame.width - 3) / 2)) { newX = (leftOprateView.frame.maxX - ((progressView.frame.width - 3) / 2)) } if newX >= (rightOprateView.frame.minX - (((progressView.frame.width - 3) / 2) + 3)) { newX = (rightOprateView.frame.minX - (((progressView.frame.width - 3) / 2) + 3)) } progressView.frame.origin.x = newX } deinit { BFLog(message: "卡点裁剪-裁剪时长视图销毁") } } extension PQVideoCutingOprateView { /// 操作手势 /// - Parameter ges: <#ges description#> /// - Returns: <#description#> @objc func panGesture(gesture: UIPanGestureRecognizer) { switch gesture.state { case .began: preOriginX = 0 currentPanView = gesture.view case .changed: if currentPanView == leftOprateView || currentPanView == rightOprateView || currentPanView == progressView { let point = gesture.translation(in: superview) var offsetX = point.x - preOriginX preOriginX = point.x if currentPanView == leftOprateView { var oprateFrame = leftOprateView.frame oprateFrame.origin.x = oprateFrame.origin.x + offsetX if oprateFrame.origin.x <= leftMargin { offsetX += leftMargin - oprateFrame.origin.x oprateFrame.origin.x = leftMargin } let minLength = rightOprateView.frame.minX - cutingOprateWidth - cutMinDuration * perSecondWidth if oprateFrame.origin.x >= minLength { offsetX -= oprateFrame.origin.x - minLength oprateFrame.origin.x = minLength } let maxLength = rightOprateView.frame.minX - cutingOprateWidth - cutMaxDuration * perSecondWidth if oprateFrame.origin.x <= maxLength { offsetX += maxLength - oprateFrame.origin.x oprateFrame.origin.x = maxLength } let time = offsetX / perSecondWidth cutStartTime = cutStartTime + time leftOprateView.frame = oprateFrame } else if currentPanView == rightOprateView { var oprateFrame = rightOprateView.frame oprateFrame.origin.x += offsetX var rightImageMaxX = leftOprateView.frame.maxX + cutMaxDuration * perSecondWidth if rightImageMaxX > frame.width - rightMargin - cutingOprateWidth { rightImageMaxX = frame.width - rightMargin - cutingOprateWidth } if oprateFrame.origin.x >= rightImageMaxX { offsetX -= oprateFrame.origin.x - rightImageMaxX oprateFrame.origin.x = rightImageMaxX } let rightImageMinX = leftOprateView.frame.maxX + cutMinDuration * perSecondWidth if oprateFrame.origin.x <= rightImageMinX { offsetX += rightImageMinX - oprateFrame.origin.x oprateFrame.origin.x = rightImageMinX } let time = offsetX / perSecondWidth cutEndTime = cutEndTime + time rightOprateView.frame = oprateFrame } else if currentPanView == progressView { var progressFrame = progressView.frame BFLog(message: "progressFrame = \(progressFrame),offsetX = \(offsetX)") progressFrame.origin.x += offsetX if progressFrame.origin.x <= (leftOprateView.frame.maxX - ((progressView.frame.width - 3) / 2)) { progressFrame.origin.x = (leftOprateView.frame.maxX - ((progressView.frame.width - 3) / 2)) } if progressFrame.origin.x >= (rightOprateView.frame.minX - (((progressView.frame.width - 3) / 2) + 3)) { progressFrame.origin.x = (rightOprateView.frame.minX - (((progressView.frame.width - 3) / 2) + 3)) } progressView.frame = progressFrame BFLog(message: "======\(progressView.frame.minX - (leftOprateView.frame.maxX - ((progressView.frame.width - 3) / 2)))") if progressDidChanged != nil { progressDidChanged!(progress) } } if currentPanView != progressView { cutTotalTime = cutEndTime - cutStartTime // 更新子视图布局 updateSubViewFrame() if cutRangeDidChanged != nil { BFLog(message: "cutStartTime = \(cutStartTime),cutEndTime = \(cutEndTime)") cutRangeDidChanged!(cutStartTime, cutEndTime, cutTotalTime) } } } case .ended: if currentPanView == leftOprateView || currentPanView == rightOprateView || currentPanView == progressView { if didEndDragging != nil { didEndDragging!(currentPanView == leftOprateView ? 1 : (currentPanView == rightOprateView ? 2 : 3), cutStartTime, cutEndTime, progress) } } currentPanView = nil default: break } } }