Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Sign in / Register
Toggle navigation
P
PhoneManager
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Yang
PhoneManager
Commits
3f3bdcb5
Commit
3f3bdcb5
authored
May 22, 2025
by
CZ1004
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
【优化】调整压缩动画以及细节
parent
87820404
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
297 additions
and
145 deletions
+297
-145
Contents.json
...xcassets/Compress/Group_1171275116.imageset/Contents.json
+23
-0
Group_1171275116.png
...s/Compress/Group_1171275116.imageset/Group_1171275116.png
+0
-0
Group_1171275116@2x.png
...ompress/Group_1171275116.imageset/Group_1171275116@2x.png
+0
-0
Group_1171275116@3x.png
...ompress/Group_1171275116.imageset/Group_1171275116@3x.png
+0
-0
CompressCompletedViewController.swift
...Compress/Controller/CompressCompletedViewController.swift
+36
-33
CompressController.swift
...r/Class/Page/Compress/Controller/CompressController.swift
+78
-1
CompressQualityController.swift
.../Page/Compress/Controller/CompressQualityController.swift
+1
-1
CompressingViewController.swift
.../Page/Compress/Controller/CompressingViewController.swift
+71
-54
CustomCompressProgressBar.swift
.../Class/Page/Compress/View/CustomCompressProgressBar.swift
+86
-54
CompressViewModel.swift
...ger/Class/Page/Compress/ViewModel/CompressViewModel.swift
+2
-2
No files found.
PhoneManager/Assets.xcassets/Compress/Group_1171275116.imageset/Contents.json
0 → 100644
View file @
3f3bdcb5
{
"images"
:
[
{
"filename"
:
"Group_1171275116.png"
,
"idiom"
:
"universal"
,
"scale"
:
"1x"
},
{
"filename"
:
"Group_1171275116@2x.png"
,
"idiom"
:
"universal"
,
"scale"
:
"2x"
},
{
"filename"
:
"Group_1171275116@3x.png"
,
"idiom"
:
"universal"
,
"scale"
:
"3x"
}
],
"info"
:
{
"author"
:
"xcode"
,
"version"
:
1
}
}
PhoneManager/Assets.xcassets/Compress/Group_1171275116.imageset/Group_1171275116.png
0 → 100644
View file @
3f3bdcb5
1.03 KB
PhoneManager/Assets.xcassets/Compress/Group_1171275116.imageset/Group_1171275116@2x.png
0 → 100644
View file @
3f3bdcb5
2.63 KB
PhoneManager/Assets.xcassets/Compress/Group_1171275116.imageset/Group_1171275116@3x.png
0 → 100644
View file @
3f3bdcb5
3.85 KB
PhoneManager/Class/Page/Compress/Controller/CompressCompletedViewController.swift
View file @
3f3bdcb5
...
...
@@ -25,7 +25,7 @@ class CompressCompletedViewController : BaseViewController{
}
}
var
comVideoDataSource
:
[
URL
?
]
=
[]{
var
comVideoDataSource
:
[
URL
]
=
[]{
didSet
{
if
let
url
=
comVideoDataSource
.
first
{
DispatchQueue
.
main
.
async
{
...
...
@@ -225,6 +225,8 @@ class CompressCompletedViewController : BaseViewController{
}
// MARK: 事件方法
/// 保留两个
@objc
func
keepButtonAction
(){
if
currentMediaType
==
.
compressPhoto
{
if
let
imageData
=
self
.
comDataSource
.
first
{
...
...
@@ -234,38 +236,34 @@ class CompressCompletedViewController : BaseViewController{
})
{
success
,
error
in
if
(
success
){
print
(
"保存照片成功"
)
}
else
{
if
let
error
=
error
{
print
(
"保存相片时出错:
\(
error
.
localizedDescription
)
"
)
}
}
self
.
jumpToRootVC
(
)
self
.
showDeleteSuccess
(
fileCount
:
1
,
fileSize
:
Int64
(
self
.
completedSize
)
)
}
}
else
{
self
.
jumpToRootVC
(
)
self
.
showDeleteSuccess
(
fileCount
:
1
,
fileSize
:
Int64
(
self
.
completedSize
)
)
}
}
else
{
// 保存视频到相册
if
let
url
=
self
.
comVideoDataSource
.
first
{
PHPhotoLibrary
.
shared
()
.
performChanges
(
{
PHAssetChangeRequest
.
creationRequestForAssetFromVideo
(
atFileURL
:
url
!
)
}
)
{
(
success
,
saveError
)
in
if
success
{
print
(
"保存视频成功"
)
}
else
{
if
let
error
=
saveError
{
print
(
"保存视频时出错:
\(
error
.
localizedDescription
)
"
)
}
PHPhotoLibrary
.
shared
()
.
performChanges
(
{
if
let
url
=
self
.
comVideoDataSource
.
first
{
PHAssetChangeRequest
.
creationRequestForAssetFromVideo
(
atFileURL
:
url
as
URL
)
}
})
{
(
success
,
saveError
)
in
if
success
{
print
(
"保存视频成功"
)
}
else
{
if
let
error
=
saveError
{
print
(
"保存视频时出错:
\(
error
.
localizedDescription
)
"
)
}
self
.
jumpToRootVC
()
}
}
else
{
self
.
jumpToRootVC
()
self
.
showDeleteSuccess
(
fileCount
:
1
,
fileSize
:
Int64
(
self
.
completedSize
))
}
}
}
/// 删除原数据
@objc
func
deleteButtonAction
(){
if
let
model
=
self
.
model
{
let
fetchResult
=
PHAsset
.
fetchAssets
(
withLocalIdentifiers
:
[
model
.
localIdentifier
],
options
:
nil
)
...
...
@@ -275,15 +273,11 @@ class CompressCompletedViewController : BaseViewController{
if
(
success
){
PhotoManager
.
shared
.
removeDataWhenDeleteInPage
(
data
:
[
self
.
model
!
])
print
(
"删除文件成功"
)
}
else
{
if
let
error
=
error
{
print
(
"删除文件时出错:
\(
error
.
localizedDescription
)
"
)
}
}
self
.
jumpToRootVC
(
)
self
.
showDeleteSuccess
(
fileCount
:
1
,
fileSize
:
Int64
(
self
.
completedSize
)
)
}
}
else
{
self
.
jumpToRootVC
(
)
self
.
showDeleteSuccess
(
fileCount
:
1
,
fileSize
:
Int64
(
self
.
completedSize
)
)
}
}
...
...
@@ -291,16 +285,25 @@ class CompressCompletedViewController : BaseViewController{
// MARK: 辅助方法
// 删除成功页面
func
showDeleteSuccess
(
fileCount
:
Int
,
fileSize
:
Int64
){
DispatchQueue
.
main
.
async
{
self
.
jumpToRootVC
()
let
vc
=
DelSuccessViewController
()
vc
.
delType
=
fileCount
>
1
?
"photos"
:
"photo"
vc
.
fileSzie
=
fileSize
vc
.
fileCount
=
fileCount
vc
.
modalPresentationStyle
=
.
fullScreen
self
.
present
(
vc
,
animated
:
true
)
}
}
/// 返回压缩控制器
func
jumpToRootVC
(){
DispatchQueue
.
main
.
async
{
if
let
viewControllers
=
self
.
navigationController
?
.
viewControllers
{
for
controller
in
viewControllers
.
reversed
()
{
if
controller
is
CompressController
{
self
.
navigationController
?
.
popToViewController
(
controller
,
animated
:
true
)
break
}
}
if
let
targetVC
=
self
.
navigationController
?
.
viewControllers
.
first
(
where
:
{
$0
is
CompressController
})
{
self
.
navigationController
?
.
popToViewController
(
targetVC
,
animated
:
true
)
}
}
}
...
...
PhoneManager/Class/Page/Compress/Controller/CompressController.swift
View file @
3f3bdcb5
...
...
@@ -153,7 +153,36 @@ extension CompressController:WaterfallMutiSectionDelegate,UICollectionViewDataSo
}
func
collectionView
(
_
collectionView
:
UICollectionView
,
didSelectItemAt
indexPath
:
IndexPath
)
{
self
.
startCompress
(
model
:
self
.
resourceData
[
indexPath
.
row
])
if
self
.
currentResourceType
==
.
compressPhoto
{
// 判断资源是否存在
let
options
=
PHImageRequestOptions
()
options
.
isNetworkAccessAllowed
=
false
options
.
progressHandler
=
nil
let
fetchResult
=
PHAsset
.
fetchAssets
(
withLocalIdentifiers
:
[
self
.
resourceData
[
indexPath
.
row
]
.
localIdentifier
],
options
:
nil
)
guard
let
phAsset
=
fetchResult
.
firstObject
else
{
showAlert
()
return
}
PHImageManager
.
default
()
.
requestImageDataAndOrientation
(
for
:
phAsset
,
options
:
options
)
{
data
,
_
,
_
,
info
in
if
data
==
nil
{
self
.
showAlert
()
}
else
{
self
.
startCompress
(
model
:
self
.
resourceData
[
indexPath
.
row
])
}
}
}
else
{
self
.
getVideoURLFromLocalIdentifier
(
localIdentifier
:
self
.
resourceData
[
indexPath
.
row
]
.
localIdentifier
)
{
url
,
error
in
if
url
!=
nil
{
self
.
startCompress
(
model
:
self
.
resourceData
[
indexPath
.
row
])
}
else
{
self
.
showAlert
()
}
}
}
}
func
referenceSizeForHeader
(
collectionView
collection
:
UICollectionView
,
layout
:
WaterfallMutiSectionFlowLayout
,
section
:
Int
)
->
CGSize
{
return
CGSize
(
width
:
self
.
collectionView
.
width
,
height
:
188
)
...
...
@@ -271,11 +300,59 @@ extension CompressController:WaterfallMutiSectionDelegate,UICollectionViewDataSo
}
func
jumpToNextPage
(
model
:
AssetModel
){
// 先将值传到下一个页面
let
vc
:
CompressQualityController
=
CompressQualityController
()
vc
.
currentMediaType
=
self
.
currentResourceType
vc
.
model
=
model
self
.
navigationController
?
.
pushViewController
(
vc
,
animated
:
true
)
}
func
showAlert
(){
let
alert
=
UIAlertController
(
title
:
nil
,
message
:
"ICloud video cannot be viewed"
,
preferredStyle
:
.
alert
)
self
.
present
(
alert
,
animated
:
true
,
completion
:
nil
)
// 2 秒后关闭弹窗
DispatchQueue
.
main
.
asyncAfter
(
deadline
:
.
now
()
+
1
)
{
alert
.
dismiss
(
animated
:
true
,
completion
:
nil
)
}
}
// 注意私有方法(某些参数不一样) 这个只能在这个类中使用
private
func
getVideoURLFromLocalIdentifier
(
localIdentifier
:
String
,
completion
:
@escaping
(
URL
?,
Error
?)
->
Void
)
{
// 通过 localIdentifier 获取 PHAsset
let
fetchOptions
=
PHFetchOptions
()
let
assets
=
PHAsset
.
fetchAssets
(
withLocalIdentifiers
:
[
localIdentifier
],
options
:
fetchOptions
)
guard
let
asset
=
assets
.
firstObject
,
asset
.
mediaType
==
.
video
else
{
DispatchQueue
.
main
.
async
{
completion
(
nil
,
NSError
(
domain
:
"com.example.error"
,
code
:
1
,
userInfo
:
[
NSLocalizedDescriptionKey
:
"未找到对应视频资源"
]))
}
return
}
let
options
=
PHVideoRequestOptions
()
options
.
isNetworkAccessAllowed
=
false
// 允许从网络下载
options
.
deliveryMode
=
.
fastFormat
// 要求高质量格式
PHImageManager
.
default
()
.
requestAVAsset
(
forVideo
:
asset
,
options
:
options
)
{
(
avAsset
,
audioMix
,
info
)
in
if
let
error
=
info
?[
PHImageErrorKey
]
as?
Error
{
DispatchQueue
.
main
.
async
{
completion
(
nil
,
error
)
}
return
}
if
let
urlAsset
=
avAsset
as?
AVURLAsset
{
DispatchQueue
.
main
.
async
{
completion
(
urlAsset
.
url
,
nil
)
}
}
else
{
DispatchQueue
.
main
.
async
{
completion
(
nil
,
NSError
(
domain
:
"CustomErrorDomain"
,
code
:
-
1
,
userInfo
:
[
NSLocalizedDescriptionKey
:
"Failed to get video URL"
]))
}
}
}
}
...
...
PhoneManager/Class/Page/Compress/Controller/CompressQualityController.swift
View file @
3f3bdcb5
...
...
@@ -258,7 +258,7 @@ class CompressQualityController : BaseViewController{
fileprivate
func
updateNextView
(
_
compressAllSize
:
Double
,
_
compressingView
:
CompressingView
,
_
comDataSource
:
[
Data
],
_
comVideoDataSource
:
[
URL
?
])
{
fileprivate
func
updateNextView
(
_
compressAllSize
:
Double
,
_
compressingView
:
CompressingView
,
_
comDataSource
:
[
Data
],
_
comVideoDataSource
:
[
URL
])
{
DispatchQueue
.
main
.
async
{
compressingView
.
removeFromSuperview
()
let
vc
=
CompressCompletedViewController
()
...
...
PhoneManager/Class/Page/Compress/Controller/CompressingViewController.swift
View file @
3f3bdcb5
...
...
@@ -22,13 +22,11 @@ class CompressingViewController: BaseViewController {
}
}
var
qualityType
:
CompressQualityType
?
{
var
qualityType
:
CompressQualityType
=
.
low
{
didSet
{
if
let
model
=
model
{
if
let
qualityType
=
qualityType
{
DispatchQueue
.
main
.
async
{
self
.
targetSizeLabel
.
text
=
formatFileSize
(
model
.
assetSize
*
qualityType
.
rawValue
)
}
DispatchQueue
.
main
.
async
{
self
.
targetSizeLabel
.
text
=
formatFileSize
(
model
.
assetSize
*
self
.
qualityType
.
rawValue
)
}
}
}
...
...
@@ -85,7 +83,7 @@ class CompressingViewController: BaseViewController {
lazy
var
progressBar
:
CustomCompressProgressBar
=
{
let
bar
=
CustomCompressProgressBar
()
bar
.
thumbImage
=
UIImage
(
systemName
:
"arrowtriangle.right.fill"
)?
.
withTintColor
(
.
red
)
bar
.
thumbImage
=
UIImage
(
named
:
"Group_1171275116"
)
bar
.
layer
.
cornerRadius
=
3.5
bar
.
clipsToBounds
=
true
return
bar
...
...
@@ -104,66 +102,85 @@ class CompressingViewController: BaseViewController {
// MARK:设置数据
// MARK: 压缩方法
/// 开始压缩
func
startCompress
(){
if
let
qualityType
=
qualityType
{
if
self
.
currentMediaType
==
.
compressPhoto
{
self
.
compressPhoto
(
model
:
self
.
model
)
}
else
{
self
.
compressVideo
(
model
:
self
.
model
)
}
}
/// 压缩单个图片
/// - Parameter model: 资源
private
func
compressPhoto
(
model
:
AssetModel
?){
if
let
model
=
model
{
var
comDataSource
:
[
Data
]
=
[]
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
)
个文件压缩失败"
)
}
manager
.
compress
(
assets
:
[
model
],
compressionQuality
:
qualityType
.
rawValue
)
{
progress
in
DispatchQueue
.
main
.
async
{
self
.
progressBar
.
updateProgress
(
to
:
CGFloat
(
progress
))
}
}
completion
:
{
compressedDataArray
,
errorArray
in
var
compressAllSize
=
0.0
for
(
_
,
data
)
in
compressedDataArray
.
enumerated
()
{
if
let
data
=
data
{
compressAllSize
=
compressAllSize
+
Double
(
data
.
count
)
comDataSource
.
append
(
data
)
}
}
self
.
progressBar
.
animationCompletion
=
{
// 不管成功失败都跳转下一页
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
(
"获取视频文件大小失败"
)
}
}
}
/// 压缩单个视频
/// - Parameter model: 资源
private
func
compressVideo
(
model
:
AssetModel
?){
if
let
model
=
model
{
var
compressAllSize
:
Double
=
0.0
CompressViewModel
.
compressVideos
(
models
:
[
model
],
quality
:
Float
(
qualityType
.
rawValue
))
{
progress
in
DispatchQueue
.
main
.
async
{
self
.
progressBar
.
updateProgress
(
to
:
CGFloat
(
progress
))
}
}
completion
:
{
(
outputURLs
,
errors
)
in
var
finallyUrls
:
[
URL
]
=
[]
for
(
_
,
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
)
}
}
else
if
let
error
=
errors
[
index
]
{
print
(
"Error compressing video
\(
index
)
:
\(
error
.
localizedDescription
)
"
)
}
catch
{
Print
(
"获取视频文件大小失败
"
)
}
finallyUrls
.
append
(
outputURL
)
}
Print
(
"---------压缩后的大小:
\(
compressAllSize
)
"
)
}
self
.
progressBar
.
animationCompletion
=
{
// 不管成功失败都跳转下一页
self
.
updateNextView
(
compressAllSize
,[],
outputURLs
)
self
.
updateNextView
(
compressAllSize
,[],
finallyUrls
)
}
}
}
}
fileprivate
func
updateNextView
(
_
compressAllSize
:
Double
,
_
comDataSource
:
[
Data
],
_
comVideoDataSource
:
[
URL
?])
{
/// 压缩结果处理
/// - Parameters:
/// - compressAllSize: 压缩后总大小
/// - comDataSource: 压缩后的图片数据数组
/// - comVideoDataSource: 压缩后的视频链接数组
fileprivate
func
updateNextView
(
_
compressAllSize
:
Double
,
_
comDataSource
:
[
Data
],
_
comVideoDataSource
:
[
URL
])
{
DispatchQueue
.
main
.
async
{
let
vc
=
CompressCompletedViewController
()
vc
.
currentMediaType
=
self
.
currentMediaType
...
...
@@ -176,7 +193,7 @@ class CompressingViewController: BaseViewController {
}
// MARK:设置UI
// MARK:
设置UI
func
setUI
(){
self
.
view
.
addSubview
(
self
.
bacImageView
)
self
.
bacImageView
.
snp
.
makeConstraints
{
make
in
...
...
@@ -222,8 +239,8 @@ class CompressingViewController: BaseViewController {
self
.
progressBar
.
snp
.
makeConstraints
{
make
in
make
.
left
.
equalTo
(
16
)
make
.
right
.
equalToSuperview
()
.
offset
(
-
16
)
make
.
height
.
equalTo
(
7
)
make
.
bottom
.
equalTo
(
-
43
-
safeHeight
)
make
.
height
.
equalTo
(
24
)
make
.
bottom
.
equalTo
(
-
34
-
safeHeight
)
}
self
.
animationView
.
play
(
fromProgress
:
0
,
toProgress
:
1
,
loopMode
:
.
loop
)
...
...
PhoneManager/Class/Page/Compress/View/CustomCompressProgressBar.swift
View file @
3f3bdcb5
...
...
@@ -8,23 +8,47 @@
import
UIKit
class
CustomCompressProgressBar
:
UIView
{
var
animationCompletion
:
(()
->
Void
)
=
{}
// 背景色(已完成进度颜色)
private
let
backgroundLayer
=
CALayer
()
// 前景色(已完成进度颜色)
private
let
foregroundLayer
=
CALayer
()
// 进度指示图标
private
let
thumbLayer
=
CALayer
()
// 当前进度值(0-1)
private
var
progress
:
CGFloat
=
0.0
// 最后一次更新时间
private
var
lastUpdateTime
:
Date
?
// 动画持续时间
private
let
animationDuration
:
CFTimeInterval
=
4.0
// 动画定时器
private
var
displayLink
:
CADisplayLink
?
private
var
animationStartTime
:
CFTimeInterval
=
0
private
var
startProgress
:
CGFloat
=
0
private
var
targetProgress
:
CGFloat
=
0
// 缩略图图片
var
thumbImage
:
UIImage
?
{
didSet
{
thumbLayer
.
contents
=
thumbImage
?
.
cgImage
thumbLayer
.
frame
=
CGRect
(
origin
:
.
zero
,
size
:
thumbImage
?
.
size
??
CGSize
(
width
:
20
,
height
:
20
))
// 调整缩略图锚点
thumbLayer
.
anchorPoint
=
CGPoint
(
x
:
0.5
,
y
:
0.5
)
// 设置正确尺寸并居中
thumbLayer
.
frame
=
CGRect
(
x
:
0
,
y
:
bounds
.
height
/
2
-
12
,
// 24/2=12
width
:
24
,
height
:
24
)
}
}
// 添加视图配置防止裁剪
override
func
didMoveToSuperview
()
{
super
.
didMoveToSuperview
()
self
.
clipsToBounds
=
false
layer
.
masksToBounds
=
false
}
override
init
(
frame
:
CGRect
)
{
super
.
init
(
frame
:
frame
)
setup
()
...
...
@@ -36,82 +60,90 @@ class CustomCompressProgressBar: UIView {
}
private
func
setup
()
{
backgroundColor
=
.
white
layer
.
cornerRadius
=
frame
.
height
/
2
layer
.
masksToBounds
=
true
// 设置背景层
backgroundLayer
.
backgroundColor
=
UIColor
(
red
:
1
,
green
:
1
,
blue
:
1
,
alpha
:
1
)
.
cgColor
backgroundLayer
.
frame
=
CGRect
(
x
:
0
,
y
:
self
.
bounds
.
height
/
2
-
3.5
,
width
:
self
.
bounds
.
width
,
height
:
7
)
backgroundLayer
.
cornerRadius
=
3.5
layer
.
addSublayer
(
backgroundLayer
)
// 设置前景层
foregroundLayer
.
backgroundColor
=
UIColor
(
red
:
0
,
green
:
0.51
,
blue
:
1
,
alpha
:
1
)
.
cgColor
foregroundLayer
.
cornerRadius
=
layer
.
cornerRadius
foregroundLayer
.
cornerRadius
=
3.5
layer
.
addSublayer
(
foregroundLayer
)
// 设置缩略图层
thumbLayer
.
contentsGravity
=
.
resizeAspect
layer
.
addSublayer
(
thumbLayer
)
}
override
func
layoutSubviews
()
{
super
.
layoutSubviews
()
CATransaction
.
begin
()
CATransaction
.
setDisableActions
(
true
)
updateLayers
(
for
:
progress
)
CATransaction
.
commit
()
}
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
startProgress
=
currentPresentationProgress
()
targetProgress
=
clampedProgress
startAnimation
()
}
private
func
currentPresentationProgress
()
->
CGFloat
{
if
let
presentation
=
foregroundLayer
.
presentation
()
{
return
presentation
.
bounds
.
width
/
bounds
.
width
}
animateProgress
(
from
:
progress
,
to
:
clampedProgress
,
duration
:
duration
)
lastUpdateTime
=
currentTime
return
progress
}
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
startAnimation
()
{
animationStartTime
=
CACurrentMediaTime
()
displayLink
?
.
invalidate
()
displayLink
=
CADisplayLink
(
target
:
self
,
selector
:
#selector(
updateAnimation
)
)
displayLink
?
.
add
(
to
:
.
main
,
forMode
:
.
common
)
}
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
)
@objc
private
func
updateAnimation
()
{
let
elapsed
=
CACurrentMediaTime
()
-
animationStartTime
let
percent
=
min
(
elapsed
/
animationDuration
,
1.0
)
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
)
let
currentProgress
=
startProgress
+
(
targetProgress
-
startProgress
)
*
CGFloat
(
percent
)
progress
=
currentProgress
progress
=
newProgress
CATransaction
.
begin
()
CATransaction
.
setDisableActions
(
true
)
foregroundLayer
.
bounds
.
size
.
width
=
newProgress
*
bounds
.
width
thumbLayer
.
position
.
x
=
(
newProgress
*
bounds
.
width
)
-
thumbLayer
.
bounds
.
width
/
2
updateLayers
(
for
:
currentProgress
)
CATransaction
.
commit
()
foregroundLayer
.
add
(
foregroundAnimation
,
forKey
:
nil
)
thumbLayer
.
add
(
thumbAnimation
,
forKey
:
nil
)
if
percent
>=
1.0
{
displayLink
?
.
invalidate
()
displayLink
=
nil
// 触发完成回调
animationCompletion
()
}
}
private
func
updateLayers
(
for
progress
:
CGFloat
)
{
let
foregroundWidth
=
bounds
.
width
*
progress
foregroundLayer
.
frame
=
CGRect
(
x
:
0
,
y
:
bounds
.
height
/
2
-
3.5
,
width
:
foregroundWidth
,
height
:
7
)
// 修改缩略图位置计算
let
thumbSize
=
thumbLayer
.
bounds
.
size
// 计算中心点位置(确保始终显示完整缩略图)
let
thumbCenterX
=
foregroundWidth
-
thumbSize
.
width
/
2
// 限制缩略图显示范围(不超出进度条边界)
let
minX
=
-
thumbSize
.
width
/
2
// 允许左侧部分超出
let
maxX
=
bounds
.
width
-
thumbSize
.
width
/
2
thumbLayer
.
frame
.
origin
.
x
=
max
(
minX
,
min
(
thumbCenterX
,
maxX
))
// 设置垂直居中
thumbLayer
.
frame
.
origin
.
y
=
bounds
.
height
/
2
-
thumbSize
.
height
/
2
}
}
PhoneManager/Class/Page/Compress/ViewModel/CompressViewModel.swift
View file @
3f3bdcb5
...
...
@@ -136,8 +136,8 @@ class CompressViewModel{
private
func
compressSingleAsset
(
_
asset
:
PHAsset
,
_
compressionQuality
:
CGFloat
,
progress
:
@escaping
(
Float
)
->
Void
,
completion
:
@escaping
(
Data
?,
Error
?)
->
Void
)
{
let
options
=
PHImageRequestOptions
()
options
.
isSynchronous
=
false
options
.
deliveryMode
=
.
highQualityFormat
// 设置为原图片
options
.
version
=
.
original
PHImageManager
.
default
()
.
requestImageDataAndOrientation
(
for
:
asset
,
options
:
options
)
{
(
imageData
,
_
,
_
,
error
)
in
guard
let
originalData
=
imageData
else
{
completion
(
nil
,
nil
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment