Commit 87820404 authored by CZ1004's avatar CZ1004

【优化】压缩

parent 9b23744d
{
"images" : [
{
"filename" : "Group.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Group@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Group@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "Group_1171275114.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Group_1171275114@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Group_1171275114@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "img_compress.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "img_compress@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "img_compress@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "img_compress.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "img_compress@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "img_compress@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "img_compress.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "img_compress@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "img_compress@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "img_compress.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "img_compress@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "img_compress@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "img_compress.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "img_compress@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "img_compress@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "img_compress.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "img_compress@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "img_compress@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "* 白色实心圆-小.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "* 白色实心圆-小@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "* 白色实心圆-小@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
......@@ -69,17 +69,6 @@ class CompressSelectCell : UICollectionViewCell {
}
}
var choose : Bool = false {
didSet{
if choose {
self.selectImageView.image = UIImage(named: "ic_unsel_com")
}else{
self.selectImageView.image = UIImage(named: "ic_sel_com")
}
callBack(self.model! ,self.choose)
}
}
lazy var backImageView: UIImageView = {
let view = UIImageView()
......@@ -125,59 +114,14 @@ class CompressSelectCell : UICollectionViewCell {
return view
}()
lazy var extensionView: UIView = {
let view = UIView()
let tap = UITapGestureRecognizer()
tap.addTarget(self, action: #selector(selectClick))
view.addGestureRecognizer(tap)
return view
}()
lazy var selectImageView: UIImageView = {
let view = UIImageView()
view.image = UIImage(named: "ic_sel_com")
view.backgroundColor = .clear
view.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer()
tap.addTarget(self, action: #selector(selectClick))
view.addGestureRecognizer(tap)
return view
}()
@objc func selectClick(){
// 判断是图片还是视频
if self.currentMediaType == .compressPhoto {
self.choose = !self.choose
}else {
let vc = PMShowImgVideoController()
vc.getVideoURLFromLocalIdentifier(localIdentifier: self.model?.localIdentifier ?? "") {[weak self] url, error in
guard let self else {return}
if url != nil{
self.choose = !self.choose
}else{
let alert = UIAlertController(title: nil, message: "ICloud video cannot be compressed", preferredStyle: .alert)
self.responderViewController()?.present(alert, animated: true, completion: nil)
// 1 秒后关闭弹窗
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
alert.dismiss(animated: true, completion: nil)
}
}
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)
self.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer()
tap.addTarget(self, action: #selector(imageClick))
self.addGestureRecognizer(tap)
// self.isUserInteractionEnabled = true
// let tap = UITapGestureRecognizer()
// tap.addTarget(self, action: #selector(imageClick))
// self.addGestureRecognizer(tap)
self.addSubview(self.backImageView)
self.backImageView.addSubview(self.playImageView)
......@@ -185,9 +129,6 @@ class CompressSelectCell : UICollectionViewCell {
self.saveSizeView.addSubview(self.saveSizeLabel)
self.saveSizeView.addSubview(self.moreImageView)
self.addSubview(self.extensionView)
self.addSubview(self.selectImageView)
self.backImageView.snp.makeConstraints { make in
make.left.right.height.width.equalToSuperview()
}
......@@ -214,16 +155,6 @@ class CompressSelectCell : UICollectionViewCell {
make.height.width.equalTo(16)
}
self.selectImageView.snp.makeConstraints { make in
make.bottom.right.equalToSuperview().offset(-12)
make.height.width.equalTo(24)
}
self.extensionView.snp.makeConstraints { make in
make.center.equalTo(self.selectImageView.snp.center)
make.height.width.equalTo(80)
}
}
required init?(coder: NSCoder) {
......@@ -231,89 +162,9 @@ class CompressSelectCell : UICollectionViewCell {
}
@objc func imageClick(){
if self.currentMediaType == .compressPhoto {
// 点击之后跳转详情页面
if let tempModel = self.model {
let vc = PMShowImgVideoController()
vc.state = .similarPhotos
vc.currentIdx = 0
vc.oldPageIndexPath = indexPath
let dataSource = ImageSeletedCollectionItem()
dataSource.isSeleted = self.choose
dataSource.id = tempModel
// 获取image
dataSource.image = PhotoAndVideoMananger.mananger.getImageFromAssetID(id: tempModel.localIdentifier)
vc.homeDataSource = [dataSource]
vc.backOrgPageCallBack = {[weak self]index,data in
guard let self else {return}
if let data = data{
if let item = data.first{
self.choose = item.isSeleted ?? false
}
}
}
self.responderViewController()?.navigationController?.pushViewController(vc, animated: true)
}
}else{
// 如果是视频
if let tempModel = self.model {
// 获取视频的图片
PhotoAndVideoMananger.mananger.getVideoImageByIdent(ident: tempModel) { image in
// 点击之后跳转详情页面
let vc = PMShowImgVideoController()
vc.state = .similarVideos
vc.currentIdx = 0
vc.oldPageIndexPath = self.indexPath
let dataSource = ImageSeletedCollectionItem()
dataSource.isSeleted = self.choose
dataSource.id = tempModel
dataSource.image = image
// 表示这个是视频
vc.homeDataSource = [dataSource]
vc.backOrgPageCallBack = {[weak self]index,data in
guard let self else {return}
if let data = data{
if let item = data.first{
self.choose = item.isSeleted ?? false
}
}
}
vc.getVideoURLFromLocalIdentifier(localIdentifier: tempModel.localIdentifier) { url, error in
if url != nil{
DispatchQueue.main.async {
vc.url = url
self.responderViewController()?.navigationController?.pushViewController(vc, animated: true)
}
}else{
let alert = UIAlertController(title: nil, message: "ICloud video cannot be viewed", preferredStyle: .alert)
self.responderViewController()?.present(alert, animated: true, completion: nil)
// 2 秒后关闭弹窗
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
alert.dismiss(animated: true, completion: nil)
}
}
}
} errorHandler: {
DispatchQueue.main.async {
let alert = UIAlertController(title: nil, message: "Get Video image failure", preferredStyle: .alert)
self.responderViewController()?.navigationController?.present(alert, animated: true)
// 1秒之后消失
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
alert.dismiss(animated: true, completion: nil)
self.responderViewController()?.navigationController?.popViewController(animated: true)
}
}
}
}
}
}
// @objc func imageClick(){
//
//
// }
}
......@@ -21,11 +21,7 @@ class CompressController : BaseViewController {
private var compressNav:CompressNavView?
// 排序
var currentSort : ResouceSortType = .largest {
didSet{
clearSelected()
}
}
var currentSort : ResouceSortType = .largest
// 资源类型 - 相册或者视频
var currentResourceType : CompressType = .compressPhoto
......@@ -39,15 +35,12 @@ class CompressController : BaseViewController {
}
}
// 当前页面选中的
var selectedModel : [AssetModel] = []
lazy var collectionView:UICollectionView = {
let layout = WaterfallMutiSectionFlowLayout()
layout.delegate = self
let sview:UICollectionView = UICollectionView.init(frame: CGRect(x: marginLR, y: self.compressNav!.height, width: self.view.width - 2 * marginLR, height: self.view.height - self.compressNav!.height - 102), collectionViewLayout: layout)
let sview:UICollectionView = UICollectionView.init(frame: CGRect(x: marginLR, y: self.compressNav!.height, width: self.view.width - 2 * marginLR, height: self.view.height - self.compressNav!.height), collectionViewLayout: layout)
sview.register(CompressSelectCell.self, forCellWithReuseIdentifier: "CompressSelectCell")
sview.register(CompressCustomHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "CompressCustomHeaderView")
sview.register( UICollectionReusableView.self,forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter,withReuseIdentifier: "footerID")
......@@ -64,18 +57,6 @@ class CompressController : BaseViewController {
}()
lazy var submitButton : UIButton = {
let view = UIButton()
view.backgroundColor = UIColor(red: 0.7, green: 0.7, blue: 0.7, alpha: 1)
view.setTitle("Compress", for: UIControl.State.normal)
view.setTitleColor(UIColor(red: 1, green: 1, blue: 1, alpha: 1), for: .normal)
view.clipsToBounds = true
view.layer.cornerRadius = 23
view.backgroundColor = UIColor(red: 0.7, green: 0.7, blue: 0.7, alpha: 1)
view.addTarget(self, action: #selector(startCompress), for: .touchUpInside)
return view
}()
func setUI(){
compressNav = CompressNavView(frame: CGRect(x: 0, y: 0, width: self.view.width, height: statusBarHeight + 44))
......@@ -85,18 +66,7 @@ class CompressController : BaseViewController {
make.top.centerX.width.equalToSuperview()
make.height.equalTo(statusBarHeight + 44)
})
self.view.insertSubview(collectionView, at: 0)
self.view.addSubview(self.submitButton)
self.submitButton.snp.makeConstraints { make in
make.top.equalTo(self.collectionView.snp.bottom).offset(16)
make.left.equalToSuperview().offset(15)
make.right.equalToSuperview().offset(-15)
make.height.equalTo(46)
}
}
override func viewDidLoad() {
......@@ -125,11 +95,6 @@ class CompressController : BaseViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// 目的是为了消除cell 的选择按钮状态
if self.selectedModel.count == 0 {
self.collectionView.reloadData()
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
......@@ -156,28 +121,6 @@ extension CompressController:WaterfallMutiSectionDelegate,UICollectionViewDataSo
cell.currentMediaType = self.currentResourceType
cell.model = self.resourceData[indexPath.row]
cell.indexPath = indexPath
if self.selectedModel.count == 0 {
cell.choose = false
}
cell.callBack = {[weak self] (model:AssetModel,choose:Bool) ->() in
guard let self else{ return}
// 这里临时管理一个当前选择的资源
if choose == true && !self.selectedModel.contains(model) {
self.selectedModel.append(model)
self.vibrate()
}
if choose == false && self.selectedModel.contains(model){
self.vibrate()
if let index = selectedModel.firstIndex(of: model) {
selectedModel.remove(at: index)
}
}
DispatchQueue.main.async {
// 选择完成后检查选择的是否有值,有值则变换底部的按钮
self.updateSubmitButton()
}
}
return cell
}
......@@ -210,8 +153,7 @@ extension CompressController:WaterfallMutiSectionDelegate,UICollectionViewDataSo
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.startCompress(model: self.resourceData[indexPath.row])
}
func referenceSizeForHeader(collectionView collection: UICollectionView, layout: WaterfallMutiSectionFlowLayout, section: Int) -> CGSize {
return CGSize (width: self.collectionView.width, height: 188)
......@@ -235,7 +177,6 @@ extension CompressController:WaterfallMutiSectionDelegate,UICollectionViewDataSo
if self.currentResourceType != flag {
self.currentResourceType = flag
// 先移除下,防止点到
self.selectedModel.removeAll()
self.resourceData.removeAll()
// 如果是图片,直接从缓存中加载
if self.currentResourceType == .compressPhoto {
......@@ -289,29 +230,8 @@ extension CompressController:WaterfallMutiSectionDelegate,UICollectionViewDataSo
}
}
func updateSubmitButton(){
if self.selectedModel.count > 0 {
self.submitButton.backgroundColor = UIColor(red: 0, green: 0.51, blue: 1, alpha: 1)
if self.currentResourceType == .compressPhoto {
self.submitButton.setTitle("Compress \(self.selectedModel.count) photos", for: UIControl.State.normal)
}else{
self.submitButton.setTitle("Compress \(self.selectedModel.count) videos", for: UIControl.State.normal)
}
self.submitButton.setTitleColor(.white, for: .normal)
}else{
self.submitButton.backgroundColor = UIColor(red: 0.7, green: 0.7, blue: 0.7, alpha: 1)
self.submitButton.setTitle("Compress", for: UIControl.State.normal)
self.submitButton.setTitleColor(UIColor(red: 1, green: 1, blue: 1, alpha: 1), for: .normal)
}
}
@objc func startCompress(){
func startCompress(model:AssetModel){
// 如果没有不进行下一步操作
if self.selectedModel.count == 0 {
return
}
if IAPManager.share.isSubscribed == false {
HomePayViewController.show {
if IAPManager.share.isSubscribed == false{
......@@ -326,21 +246,22 @@ extension CompressController:WaterfallMutiSectionDelegate,UICollectionViewDataSo
self.present(alertView, animated: true)
alertView.selectClickCallback = {isSure in
self.view.hideBlur()
alertView.dismiss(animated: true)
alertView.dismiss(animated: true) {
if isSure {
self.popAdverTisement()
AdvManager.shared.finisedCallBack = {
self.jumpToNextPage()
self.jumpToNextPage(model: model)
}
}
}
}
}else {
self.jumpToNextPage()
self.jumpToNextPage(model: model)
}
}
}else {
self.jumpToNextPage()
self.jumpToNextPage(model: model)
}
}
......@@ -349,27 +270,12 @@ extension CompressController:WaterfallMutiSectionDelegate,UICollectionViewDataSo
AdvManager.shared.showInterstitialAd(vc: self)
}
func jumpToNextPage(){
func jumpToNextPage(model:AssetModel){
// 先将值传到下一个页面
let vc : CompressQualityController = CompressQualityController()
vc.currentMediaType = self.currentResourceType
vc.model = self.selectedModel
if self.currentResourceType == .compressPhoto{
vc.detailTiplabel.text = "You've selected \(self.selectedModel.count) photos(A total of \(self.resourceData.count)) to compress."
}else{
vc.detailTiplabel.text = "You've selected \(self.selectedModel.count) videos(A total of \(self.resourceData.count)) to compress."
}
vc.model = model
self.navigationController?.pushViewController(vc, animated: true)
// 然后清理下当前页面的值
clearSelected()
}
func clearSelected() {
self.selectedModel.removeAll()
self.updateSubmitButton()
}
......
//
// CompressingViewController.swift
// PhoneManager
//
// Created by edy on 2025/5/21.
//
import UIKit
import Lottie
class CompressingViewController: BaseViewController {
var currentMediaType : CompressType = .compressPhoto
var model : AssetModel? {
didSet{
if let model = model {
DispatchQueue.main.async {
self.orgSizeLabel.text = formatFileSize(model.assetSize)
}
}
}
}
var qualityType : CompressQualityType? {
didSet{
if let model = model {
if let qualityType = qualityType {
DispatchQueue.main.async {
self.targetSizeLabel.text = formatFileSize(model.assetSize * qualityType.rawValue)
}
}
}
}
}
lazy var bacImageView : UIImageView = {
let view = UIImageView()
view.image = UIImage(named: "Group_1171275114")
return view
}()
lazy var animationViewBacView : UIImageView = {
let view = UIImageView()
view.image = UIImage(named: "* 白色实心圆-小")
view.backgroundColor = .clear
return view
}()
lazy var animationView : LottieAnimationView = {
let animationView = LottieAnimationView(name: "iOS压缩完成")
animationView.layer.cornerRadius = 12
animationView.backgroundColor = .clear
animationView.loopMode = .loop
return animationView
}()
lazy var tipLabel : UILabel = {
let label = UILabel()
label.text = "We're freeing up space on your device!"
label.textColor = UIColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1)
label.font = UIFont.systemFont(ofSize: 18, weight: .medium)
label.textAlignment = .center
label.numberOfLines = 0
return label
}()
lazy var orgSizeLabel : UILabel = {
let label = UILabel()
label.textColor = UIColor(red: 0.07, green: 0.07, blue: 0.07, alpha: 1)
label.font = UIFont.systemFont(ofSize: 14, weight: .medium)
label.textAlignment = .left
return label
}()
lazy var targetSizeLabel : UILabel = {
let label = UILabel()
label.textColor = UIColor(red: 0, green: 0.51, blue: 1, alpha: 1)
label.font = UIFont.systemFont(ofSize: 14, weight: .medium)
label.textAlignment = .right
return label
}()
lazy var progressBar : CustomCompressProgressBar = {
let bar = CustomCompressProgressBar()
bar.thumbImage = UIImage(systemName: "arrowtriangle.right.fill")?.withTintColor(.red)
bar.layer.cornerRadius = 3.5
bar.clipsToBounds = true
return bar
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor(red: 0.95, green: 0.96, blue: 0.99, alpha: 1)
self.titleView.isHidden = true
setUI()
startCompress()
}
// MARK:设置数据
func startCompress(){
if let qualityType = qualityType {
let manager : CompressViewModel = CompressViewModel()
if self.currentMediaType == .compressPhoto {
// 表示压缩图片
var comDataSource : [Data] = []
manager.compress(assets: [self.model!], compressionQuality: qualityType.rawValue) {progress in
Print(progress)
DispatchQueue.main.async {
self.progressBar.updateProgress(to: CGFloat(progress))
}
} completion: { compressedDataArray, errorArray in
var compressAllSize = 0.0
for (index, data) in compressedDataArray.enumerated() {
if let error = errorArray[index] {
print("第 \(index + 1) 个文件压缩出错: \(error.localizedDescription)")
} else if let data = data {
print("第 \(index + 1) 个文件压缩完成,压缩后大小: \(data.count) 字节")
compressAllSize = compressAllSize + Double(data.count)
comDataSource.append(data)
} else {
print("第 \(index + 1) 个文件压缩失败")
}
}
self.updateNextView(compressAllSize,comDataSource,[])
}
}else{
// 压缩视频
var compressAllSize : Double = 0.0
CompressViewModel.compressVideos(models: [self.model!], quality: Float(qualityType.rawValue)) { progress in
DispatchQueue.main.async {
self.progressBar.updateProgress(to: CGFloat(progress))
}
} completion: { (outputURLs, errors) in
for (index, outputURL) in outputURLs.enumerated() {
if let outputURL = outputURL {
do {
let attributes = try FileManager.default.attributesOfItem(atPath: outputURL.path)
if let fileSize = attributes[.size] as? Int64 {
compressAllSize = compressAllSize + Double(fileSize)
}
} catch {
Print("获取视频文件大小失败")
}
} else if let error = errors[index] {
print("Error compressing video \(index): \(error.localizedDescription)")
}
}
Print("---------压缩后的大小:\(compressAllSize)")
// 不管成功失败都跳转下一页
self.updateNextView(compressAllSize,[],outputURLs)
}
}
}
}
fileprivate func updateNextView(_ compressAllSize: Double,_ comDataSource : [Data],_ comVideoDataSource : [URL?]) {
DispatchQueue.main.async {
let vc = CompressCompletedViewController()
vc.currentMediaType = self.currentMediaType
vc.comDataSource = comDataSource
vc.comVideoDataSource = comVideoDataSource
vc.model = self.model
vc.completedSize = compressAllSize
self.navigationController?.pushViewController(vc, animated: true)
}
}
// MARK:设置UI
func setUI(){
self.view.addSubview(self.bacImageView)
self.bacImageView.snp.makeConstraints { make in
make.height.equalTo(211)
make.width.equalTo(264.7)
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(96 + statusBarHeight)
}
self.view.addSubview(self.animationViewBacView)
self.animationViewBacView.snp.makeConstraints { make in
make.top.equalTo(self.bacImageView.snp.top).offset(58)
make.width.height.equalTo(108.75)
make.centerX.equalToSuperview()
}
self.view.addSubview(self.animationView)
self.animationView.snp.makeConstraints { make in
make.width.height.equalTo(145)
make.top.equalTo(self.bacImageView.snp.top).offset(40)
make.centerX.equalToSuperview()
}
self.view.addSubview(self.tipLabel)
self.tipLabel.snp.makeConstraints { make in
make.width.equalTo(304)
make.height.equalTo(50)
make.centerX.equalToSuperview()
make.top.equalTo(self.bacImageView.snp.bottom).offset(96)
}
self.view.addSubview(self.orgSizeLabel)
self.orgSizeLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.height.equalTo(22)
make.bottom.equalTo(-66 - safeHeight)
}
self.view.addSubview(self.targetSizeLabel)
self.targetSizeLabel.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-16)
make.height.equalTo(22)
make.bottom.equalTo(-66 - safeHeight)
}
self.view.addSubview(self.progressBar)
self.progressBar.snp.makeConstraints { make in
make.left.equalTo(16)
make.right.equalToSuperview().offset(-16)
make.height.equalTo(7)
make.bottom.equalTo(-43 - safeHeight)
}
self.animationView.play(fromProgress: 0, toProgress: 1 , loopMode: .loop)
}
}
......@@ -10,7 +10,7 @@ import Foundation
class CompressingView: UIView{
var data: [AssetModel]?
var data: AssetModel?
override init(frame: CGRect) {
super.init(frame: frame)
......
//
// CustomProgressBar.swift
// PhoneManager
//
// Created by edy on 2025/5/21.
//
import UIKit
class CustomCompressProgressBar: UIView {
// 前景色(已完成进度颜色)
private let foregroundLayer = CALayer()
// 进度指示图标
private let thumbLayer = CALayer()
// 当前进度值(0-1)
private var progress: CGFloat = 0.0
// 最后一次更新时间
private var lastUpdateTime: Date?
// 缩略图图片
var thumbImage: UIImage? {
didSet {
thumbLayer.contents = thumbImage?.cgImage
thumbLayer.frame = CGRect(origin: .zero, size: thumbImage?.size ?? CGSize(width: 20, height: 20))
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
private func setup() {
backgroundColor = .white
layer.cornerRadius = frame.height / 2
layer.masksToBounds = true
// 设置前景层
foregroundLayer.backgroundColor = UIColor(red: 0, green: 0.51, blue: 1, alpha: 1).cgColor
foregroundLayer.cornerRadius = layer.cornerRadius
layer.addSublayer(foregroundLayer)
// 设置缩略图层
thumbLayer.contentsGravity = .resizeAspect
layer.addSublayer(thumbLayer)
}
override func layoutSubviews() {
super.layoutSubviews()
updateLayers(for: progress)
}
func updateProgress(to newProgress: CGFloat) {
let currentTime = Date()
let clampedProgress = max(0, min(newProgress, 1.0))
var duration: TimeInterval = 0
if let lastTime = lastUpdateTime {
let timeSinceLast = currentTime.timeIntervalSince(lastTime)
duration = timeSinceLast < 1.0 ? 1.0 : timeSinceLast
}
animateProgress(from: progress, to: clampedProgress, duration: duration)
lastUpdateTime = currentTime
}
private func updateLayers(for progress: CGFloat) {
let foregroundWidth = bounds.width * progress
foregroundLayer.frame = CGRect(x: 0, y: 0, width: foregroundWidth, height: bounds.height)
let thumbSize = thumbLayer.bounds.size
let thumbX = progress * bounds.width - thumbSize.width/2
thumbLayer.frame.origin = CGPoint(
x: max(0, min(thumbX, bounds.width - thumbSize.width)),
y: (bounds.height - thumbSize.height)/2
)
}
private func animateProgress(from oldProgress: CGFloat, to newProgress: CGFloat, duration: TimeInterval) {
foregroundLayer.removeAllAnimations()
thumbLayer.removeAllAnimations()
guard duration > 0 else {
progress = newProgress
updateLayers(for: newProgress)
return
}
let foregroundAnimation = CABasicAnimation(keyPath: "bounds.size.width")
foregroundAnimation.fromValue = oldProgress * bounds.width
foregroundAnimation.toValue = newProgress * bounds.width
foregroundAnimation.duration = duration
foregroundAnimation.timingFunction = CAMediaTimingFunction(name: .linear)
let thumbAnimation = CABasicAnimation(keyPath: "position.x")
thumbAnimation.fromValue = (oldProgress * bounds.width) - thumbLayer.bounds.width/2
thumbAnimation.toValue = (newProgress * bounds.width) - thumbLayer.bounds.width/2
thumbAnimation.duration = duration
thumbAnimation.timingFunction = CAMediaTimingFunction(name: .linear)
progress = newProgress
CATransaction.begin()
CATransaction.setDisableActions(true)
foregroundLayer.bounds.size.width = newProgress * bounds.width
thumbLayer.position.x = (newProgress * bounds.width) - thumbLayer.bounds.width/2
CATransaction.commit()
foregroundLayer.add(foregroundAnimation, forKey: nil)
thumbLayer.add(thumbAnimation, forKey: nil)
}
}
......@@ -9,15 +9,6 @@ import UIKit
class VideoPlayView: UIView {
var videoUrl : URL? {
didSet {
if let url = videoUrl {
self.videoView.playVideo(from: url)
self.videoView.pause()
}
}
}
lazy var playImageView : UIImageView = {
let imageView = UIImageView()
imageView.backgroundColor = .clear
......@@ -28,18 +19,19 @@ class VideoPlayView: UIView {
lazy var timeLabel : UILabel = {
let label = UILabel()
label.textColor = .white
label.text = "00:00"
label.text = "00:25"
label.font = UIFont.systemFont(ofSize: 14, weight: .semibold)
label.textAlignment = .right
return label
}()
lazy var videoView : SecretVideoPlayer = {
let view = SecretVideoPlayer()
lazy var videoView : UIImageView = {
let view = UIImageView()
view.clipsToBounds = true
view.layer.cornerRadius = 12
view.backgroundColor = UIColor(red: 0.95, green: 0.96, blue: 0.99, alpha: 1)
view.contentMode = .scaleAspectFit
view.backgroundColor = .clear
return view
}()
......@@ -57,25 +49,6 @@ class VideoPlayView: UIView {
make.top.equalToSuperview().offset(8)
make.height.equalTo(20)
}
self.videoView.callBackCurrtntTimeString = {[weak self]text in
guard let self else {return}
DispatchQueue.main.async {
self.timeLabel.text = text
}
}
self.videoView.tapPauseCallback = {[weak self] state in
guard let self else {return}
if state == .playing {
self.playImageView.isHidden = true
}
if state == .pause {
self.playImageView.isHidden = false
}
if state == .end {
self.playImageView.isHidden = false
self.timeLabel.text = "00:00"
}
}
self.addSubview(self.playImageView)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment