Commit 253a5552 authored by yqz's avatar yqz

Merge branch 'develop_0409' into yQz0507

* develop_0409:
  【优化】部分功能新增优化
  【新增】垃圾桶详情以及部分优化
  【新增】添加视频、截图、其他选择按钮
  【新增】撤回功能,照片详情页面
  【优化】处理部分逻辑
  【新增】左滑垃圾桶功能
  【新增】垃圾桶功能
  【新增】内部勾选框
parents 73b3ed19 38989c96
This diff is collapsed.
......@@ -55,6 +55,40 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
return true
}
func applicationWillTerminate(_ application: UIApplication) {
// 保存单利中所有垃圾桶的数据
saveDataToTrashDB(data: Singleton.shared.trashData)
// 保存单利中所有垃圾桶的数据
saveDataToKeepListhDB(data: Singleton.shared.keepList)
}
func saveDataToTrashDB(data : [TrashTypeEnum : [AssetModel]]){
for (key,value) in data {
for item in value {
let success = TrashDatabase.shared.insert(localIdentifier: item.localIdentifier, assetSize: item.assetSize, createDate: item.createDate, mediaType: key.dbType)
if !success {
Print("保存单利数据到数据库失败")
}
}
}
Singleton.shared.trashData = [:]
}
func saveDataToKeepListhDB(data : [TrashTypeEnum : [AssetModel]]){
for (key,value) in data {
let uniqueId = UUID().uuidString
for item in value {
let success = GroupDatabase.shared.insert(localIdentifier: item.localIdentifier, assetSize: item.assetSize, createDate: item.createDate, mediaType: key == TrashTypeEnum.video ? 2 : 1,groupId: uniqueId)
if !success {
Print("保存保留列表数据失败")
}
}
}
Singleton.shared.keepList = [:]
}
func applicationDidBecomeActive(_ application: UIApplication) {
NotificationCenter.default.post(name: Notification.Name("applicationDidBecomeActive"), object: nil)
......
{
"images" : [
{
"filename" : "icon_dingyue.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon_dingyue@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon_dingyue@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "Frame.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Frame@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Frame@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "Frame.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Frame@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Frame@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "Frame.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Frame@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Frame@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "Frame.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Frame@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Frame@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "Group_1171275102.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Group_1171275102@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Group_1171275102@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
This diff is collapsed.
......@@ -198,6 +198,64 @@ class TrashDatabase {
return result
}
// 批量删除数据
func batchDelete(localIdentifiers: [String]) -> Bool {
// 使用事务来确保原子性
let beginTransaction = "BEGIN TRANSACTION;"
let commitTransaction = "COMMIT TRANSACTION;"
let rollbackTransaction = "ROLLBACK TRANSACTION;"
// 准备删除语句
let deleteStatementString = "DELETE FROM trash WHERE localIdentifier = ?;"
var deleteStatement: OpaquePointer?
// 开始事务
if sqlite3_exec(db, beginTransaction, nil, nil, nil) != SQLITE_OK {
print("开始事务失败")
return false
}
// 准备删除语句
if sqlite3_prepare_v2(db, deleteStatementString, -1, &deleteStatement, nil) == SQLITE_OK {
// 遍历所有需要删除的标识符
for identifier in localIdentifiers {
// 重置语句以重新使用
sqlite3_reset(deleteStatement)
// 绑定参数
sqlite3_bind_text(deleteStatement, 1, (identifier as NSString).utf8String, -1, nil)
// 执行删除
if sqlite3_step(deleteStatement) != SQLITE_DONE {
print("删除数据失败: \(identifier)")
// 如果有任何失败,回滚事务
sqlite3_exec(db, rollbackTransaction, nil, nil, nil)
sqlite3_finalize(deleteStatement)
return false
}
}
// 完成后释放语句
sqlite3_finalize(deleteStatement)
// 提交事务
if sqlite3_exec(db, commitTransaction, nil, nil, nil) == SQLITE_OK {
print("成功批量删除数据")
return true
} else {
print("提交事务失败")
sqlite3_exec(db, rollbackTransaction, nil, nil, nil)
return false
}
}
// 如果准备语句失败
print("准备删除语句失败")
sqlite3_exec(db, rollbackTransaction, nil, nil, nil)
return false
}
deinit {
sqlite3_close(db)
}
......
......@@ -26,7 +26,7 @@ class ChargeGuideController : BaseViewController,UIScrollViewDelegate,UINavigati
"Scroll the page and select Charger section",
"Select 'ls Connected' and 'Run lmmediately',then tap on Next",
"Tap on New Blank Automation to connect the app",
"Type 'Cleanup' & select Run Charging Animation and tap on Done!"
"Type 'PhoneManager' & select Run Charging Animation and tap on Done!"
]
let numberOfPages = 7
......
......@@ -17,6 +17,8 @@ typealias CompressSelectCellCallback = (AssetModel,Bool)->Void
class CompressSelectCell : UICollectionViewCell {
var indexPath : IndexPath?
var callBack : CompressSelectCellCallback = {model,choose in}
var currentMediaType : CompressType = .compressPhoto {
......@@ -146,6 +148,10 @@ class CompressSelectCell : UICollectionViewCell {
@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}
......@@ -161,6 +167,7 @@ class CompressSelectCell : UICollectionViewCell {
}
}
}
}
override init(frame: CGRect) {
......@@ -225,23 +232,31 @@ 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 = true
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{
......@@ -253,12 +268,21 @@ class CompressSelectCell : UICollectionViewCell {
let vc = PMShowImgVideoController()
vc.state = .similarVideos
vc.currentIdx = 0
vc.oldPageIndexPath = self.indexPath
let dataSource = ImageSeletedCollectionItem()
dataSource.isSeleted = true
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 {
......
......@@ -19,8 +19,16 @@ class CompressCompletedViewController : BaseViewController{
var currentMediaType : CompressType = .compressPhoto
lazy var bacView : UIView = {
let view = UIView()
view.layer.cornerRadius = 115
view.backgroundColor = UIColor(red: 0, green: 0.51, blue: 1, alpha: 1)
return view
}()
lazy var animationView : LottieAnimationView = {
let animationView = LottieAnimationView(name: "CompressCompletedLight")
let animationView = LottieAnimationView(name: "iOS压缩完成")
animationView.layer.cornerRadius = 12
animationView.backgroundColor = .clear
animationView.loopMode = .loop
......@@ -153,6 +161,7 @@ class CompressCompletedViewController : BaseViewController{
self.view.backgroundColor = UIColor(red: 0.95, green: 0.96, blue: 0.99, alpha: 1)
self.titleView.isHidden = true
self.view.addSubview(self.bacView)
self.view.addSubview(self.animationView)
self.view.addSubview(self.tipLabel)
self.view.addSubview(self.detailTiplabel)
......@@ -168,7 +177,11 @@ class CompressCompletedViewController : BaseViewController{
self.infoView.addSubview(self.finalTipLabel)
self.view.addSubview(self.completedButton)
self.bacView.snp.makeConstraints { make in
make.top.equalTo(statusBarHeight + 60)
make.width.height.equalTo(230)
make.centerX.equalToSuperview()
}
self.animationView.snp.makeConstraints { make in
make.top.equalTo(statusBarHeight + 60)
make.width.height.equalTo(230)
......@@ -366,7 +379,7 @@ class CompressCompletedViewController : BaseViewController{
override func viewDidLoad() {
super.viewDidLoad()
setUI()
self.animationView.play(fromProgress: 0, toProgress: 1,loopMode: .playOnce)
self.animationView.play(fromProgress: 0, toProgress: 1,loopMode: .loop)
}
......
......@@ -174,6 +174,7 @@ extension CompressController:WaterfallMutiSectionDelegate,UICollectionViewDataSo
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CompressSelectCell", for: indexPath) as! CompressSelectCell
cell.currentMediaType = self.currentResourceType
cell.model = self.resourceData[indexPath.row]
cell.indexPath = indexPath
if self.selectedModel.count == 0 {
cell.choose = false
}
......@@ -255,6 +256,7 @@ extension CompressController:WaterfallMutiSectionDelegate,UICollectionViewDataSo
if self.currentResourceType != flag {
self.currentResourceType = flag
// 先移除下,防止点到
self.selectedModel.removeAll()
self.resourceData.removeAll()
// 如果是图片,直接从缓存中加载
if self.currentResourceType == .compressPhoto {
......@@ -366,9 +368,13 @@ extension CompressController:WaterfallMutiSectionDelegate,UICollectionViewDataSo
func jumpToNextPage(){
// 先将值传到下一个页面
let vc : CompressQualityController = CompressQualityController()
vc.model = self.selectedModel
vc.currentMediaType = self.currentResourceType
vc.detailTiplabel.text = "You've selected \(self.selectedModel.count) out of \(self.resourceData.count) photos to compress."
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."
}
self.navigationController?.pushViewController(vc, animated: true)
// 然后清理下当前页面的值
......
......@@ -7,29 +7,70 @@
import Foundation
/// 压缩多少枚举
enum CompressQualityType {
// 压缩少量
case low
// 压缩中等
case mid
// 压缩较多
case high
}
class CompressQualityController : BaseViewController{
var model : [AssetModel]? {
didSet{
let ident = model!.first!.localIdentifier
if let model = model {
if let modelFirst = model.first {
if self.currentMediaType == .compressPhoto {
let ident = modelFirst.localIdentifier
let image = PhotoAndVideoMananger.mananger.getImageFromAssetID(id: ident)
self.imageView.image = image
}else {
// 获取视频的URL
PhotoAndVideoMananger.mananger.getVideoURLFromLocalIdentifier(localIdentifier: modelFirst.localIdentifier) { url, error in
if let url = url {
self.videoView.videoUrl = url
}
}
}
}
}
}
}
var currentQulityType : Int = 0
var currentMediaType : CompressType = .compressPhoto
var currentMediaType : CompressType = .compressPhoto {
didSet{
self.imageView.isHidden = currentMediaType == .compressVideo
self.videoView.isHidden = currentMediaType == .compressPhoto
}
}
private var compressNav:CompressNavView?
lazy var videoView : VideoPlayView = {
let view = VideoPlayView()
view.clipsToBounds = true
view.layer.cornerRadius = 12
view.backgroundColor = UIColor(red: 0.95, green: 0.96, blue: 0.99, alpha: 1)
return view
}()
lazy var imageView: UIImageView = {
let imageView = UIImageView()
imageView.clipsToBounds = true
imageView.layer.cornerRadius = 12
imageView.contentMode = .scaleAspectFit
imageView.contentMode = .scaleAspectFill
imageView.backgroundColor = UIColor(red: 0.95, green: 0.96, blue: 0.99, alpha: 1)
return imageView
}()
......@@ -46,7 +87,7 @@ class CompressQualityController : BaseViewController{
lazy var detailTiplabel: UILabel = {
let label = UILabel()
label.text = "You've selected 2 out of 253 photos to compress."
label.text = "You've selected 2 photos(A total of 17) to compress."
label.textAlignment = .left
label.font = UIFont.systemFont(ofSize: 12, weight: .regular)
label.backgroundColor = .clear
......@@ -56,6 +97,7 @@ class CompressQualityController : BaseViewController{
let view = QualityView()
view.type = 0
view.selectedImageView.image = UIImage(named: "ic_unsel_com")
view.layer.borderWidth = 1
return view
}()
......@@ -76,6 +118,14 @@ class CompressQualityController : BaseViewController{
}()
lazy var buttonBacView: UIView = {
let view = UIView()
view.backgroundColor = .white
view.addTopShadow()
return view
}()
lazy var submitButton : UIButton = {
let view = UIButton()
view.setTitle("Compress", for: UIControl.State.normal)
......@@ -95,13 +145,15 @@ class CompressQualityController : BaseViewController{
make.top.centerX.width.equalToSuperview()
make.height.equalTo(statusBarHeight + 44)
})
self.view.addSubview(self.videoView)
self.view.addSubview(self.imageView)
self.view.addSubview(self.tipLabel)
self.view.addSubview(self.detailTiplabel)
self.view.addSubview(self.lowQualityView)
self.view.addSubview(self.mediumQualityView)
self.view.addSubview(self.highQualityView)
self.view.addSubview(self.submitButton)
self.view.addSubview(self.buttonBacView)
self.buttonBacView.addSubview(self.submitButton)
self.imageView.snp.makeConstraints { make in
make.top.equalTo(self.compressNav!.height + 20)
......@@ -109,6 +161,13 @@ class CompressQualityController : BaseViewController{
make.right.equalToSuperview().offset(-15)
make.height.equalTo(259)
}
self.videoView.snp.makeConstraints { make in
make.top.equalTo(self.compressNav!.height + 20)
make.left.equalToSuperview().offset(15)
make.right.equalToSuperview().offset(-15)
make.height.equalTo(259)
}
self.tipLabel.snp.makeConstraints { make in
make.top.equalTo(self.imageView.snp.bottom).offset(28)
make.left.equalToSuperview().offset(15)
......@@ -140,11 +199,19 @@ class CompressQualityController : BaseViewController{
make.height.equalTo(61)
}
self.buttonBacView.snp.makeConstraints { make in
make.bottom.equalTo(-safeHeight)
make.left.equalToSuperview()
make.right.equalToSuperview()
make.height.equalTo(68)
}
self.submitButton.snp.makeConstraints { make in
make.bottom.equalTo(-40)
make.left.equalToSuperview().offset(15)
make.right.equalToSuperview().offset(-15)
make.height.equalTo(46)
make.centerY.equalToSuperview()
}
}
......@@ -161,25 +228,61 @@ class CompressQualityController : BaseViewController{
guard let self else {return}
self.currentQulityType = 0
self.lowQualityView.selectedImageView.image = UIImage(named: "ic_unsel_com")
self.lowQualityView.layer.borderWidth = 1
self.mediumQualityView.layer.borderWidth = 0
self.highQualityView.layer.borderWidth = 0
self.mediumQualityView.selectedImageView.image = UIImage(named: "ic_sel_com")
self.highQualityView.selectedImageView.image = UIImage(named: "ic_sel_com")
setButtonTitleByType(type: .low)
}
self.mediumQualityView.callBack = {[weak self] type in
guard let self else {return}
self.currentQulityType = 1
self.lowQualityView.selectedImageView.image = UIImage(named: "ic_sel_com")
self.lowQualityView.layer.borderWidth = 0
self.mediumQualityView.layer.borderWidth = 1
self.highQualityView.layer.borderWidth = 0
self.mediumQualityView.selectedImageView.image = UIImage(named: "ic_unsel_com")
self.highQualityView.selectedImageView.image = UIImage(named: "ic_sel_com")
setButtonTitleByType(type: .mid)
}
self.highQualityView.callBack = {[weak self] type in
guard let self else {return}
self.currentQulityType = 2
self.lowQualityView.selectedImageView.image = UIImage(named: "ic_sel_com")
self.lowQualityView.layer.borderWidth = 0
self.mediumQualityView.layer.borderWidth = 0
self.highQualityView.layer.borderWidth = 1
self.mediumQualityView.selectedImageView.image = UIImage(named: "ic_sel_com")
self.highQualityView.selectedImageView.image = UIImage(named: "ic_unsel_com")
setButtonTitleByType(type: .high)
}
// 设置默认值
setButtonTitleByType(type: .low)
}
func setButtonTitleByType(type : CompressQualityType){
var size : Double = 0.0
if let array = self.model{
for item in array {
size = size + item.assetSize
}
}
// 求和之后看点击的是哪个
var compressSize : Double = 0.0
if type == .low {
compressSize = size * 0.2
}else if type == .mid {
compressSize = size * 0.5
}else {
compressSize = size * 0.8
}
self.submitButton.setTitle("Compress \(formatFileSize(compressSize))", for: .normal)
}
fileprivate func updateNextView(_ compressAllSize: Double, _ compressingView: CompressingView,_ comDataSource : [Data],_ comVideoDataSource : [URL?]) {
......
......@@ -9,6 +9,7 @@ import Foundation
class CompressNavView : UIView {
private var backButton:UIButton!
private var titleLabel:UILabel!
private var proBtn:UIButton!
override init(frame: CGRect) {
......@@ -38,13 +39,27 @@ class CompressNavView : UIView {
make.width.height.equalTo(iconWH)
}
titleLabel = UILabel()
titleLabel.text = "Compress"
titleLabel.font = UIFont.systemFont(ofSize: 20, weight: .bold)
titleLabel.textColor = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1)
titleLabel.textAlignment = .center
self.addSubview(titleLabel)
proBtn = UIButton(frame: CGRect(x: 0, y: 0, width: 70, height: iconWH))
proBtn.setBackgroundImage(UIImage(named: "home_pro_star_back"), for: .normal)
proBtn.setBackgroundImage(UIImage(named: "ic_pro_home"), for: .normal)
proBtn.addTarget(self, action: #selector(proBtnClick), for: .touchUpInside)
self.addSubview(proBtn)
proBtn.snp.makeConstraints { make in
titleLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.centerY.equalTo(navCenterY)
make.width.equalTo(100)
make.height.equalTo(28)
}
proBtn.snp.makeConstraints { make in
make.centerY.equalTo(navCenterY)
make.right.equalToSuperview().offset(-15)
make.width.equalTo(70)
......
......@@ -41,6 +41,14 @@ class QualityView : UIView{
make.width.equalTo(24)
make.height.equalTo(24)
}
self.layer.borderColor = UIColor(red: 0, green: 0.51, blue: 1, alpha: 1).cgColor
// 给自身添加手势
let tap = UITapGestureRecognizer()
tap.addTarget(self, action: #selector(selectClick))
self.isUserInteractionEnabled = true
self.addGestureRecognizer(tap)
}
required init?(coder: NSCoder) {
......@@ -51,6 +59,7 @@ class QualityView : UIView{
let label = UILabel()
label.text = "Low quality"
label.textAlignment = .left
label.textColor = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1)
label.font = UIFont.systemFont(ofSize: 14, weight: .bold)
label.backgroundColor = .clear
return label
......@@ -60,6 +69,7 @@ class QualityView : UIView{
let label = UILabel()
label.text = "Compress up to 80% of the size"
label.textAlignment = .left
label.textColor = UIColor(red: 0.4, green: 0.4, blue: 0.4, alpha: 1)
label.font = UIFont.systemFont(ofSize: 12, weight: .regular)
label.backgroundColor = .clear
return label
......
//
// VideoPlayView.swift
// PhoneManager
//
// Created by edy on 2025/5/15.
//
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
imageView.image = UIImage(named: "playImage")
return imageView
}()
lazy var timeLabel : UILabel = {
let label = UILabel()
label.textColor = .white
label.text = "00:00"
label.font = UIFont.systemFont(ofSize: 14, weight: .semibold)
label.textAlignment = .right
return label
}()
lazy var videoView : SecretVideoPlayer = {
let view = SecretVideoPlayer()
view.clipsToBounds = true
view.layer.cornerRadius = 12
view.backgroundColor = UIColor(red: 0.95, green: 0.96, blue: 0.99, alpha: 1)
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.addSubview(self.videoView)
self.videoView.snp.makeConstraints { make in
make.left.top.right.bottom.equalToSuperview()
}
self.addSubview(self.timeLabel)
self.timeLabel.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-8)
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)
self.playImageView.snp.makeConstraints { make in
make.width.height.equalTo(18)
make.center.equalToSuperview()
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
//
// VideoViewController.swift
// PhoneManager
//
// Created by edy on 2025/5/13.
//
import Foundation
import AVKit
import MediaPlayer
class VideoViewController: UIViewController {
var url : URL?
var isAutoPlay : Bool = true
// 系统播放器控制器
let playerViewController = AVPlayerViewController()
override func viewDidLoad() {
super.viewDidLoad()
setupPlayer()
}
func setupPlayer() {
// 1. 创建播放器
if let url = self.url {
let player = AVPlayer(url: url)
// 2. 配置播放器控制器
playerViewController.player = player
playerViewController.delegate = self
playerViewController.showsPlaybackControls = true
// 3. 添加到当前视图
addChild(playerViewController)
view.addSubview(playerViewController.view)
playerViewController.view.frame = view.bounds
playerViewController.didMove(toParent: self)
// 4. 自动播放
if self.isAutoPlay {
player.play()
}
}
}
}
// MARK: - AVPlayerViewControllerDelegate
extension VideoViewController: AVPlayerViewControllerDelegate {
// 处理全屏切换
func playerViewController(_ playerViewController: AVPlayerViewController,
willBeginFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator) {
}
func playerViewController(_ playerViewController: AVPlayerViewController,
willEndFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator) {
}
}
......@@ -39,7 +39,8 @@ class HomePhotosDetailViewController : BaseViewController {
for item in self.model.assets {
dataArray = dataArray + item
}
self.resourceData = dataArray
self.resourceData = self.filterTrashData(array: dataArray)
self.selectedModel = self.filterTrashData(array: self.selectedModel)
self.sortByType(sortType: self.currentSort)
}
......@@ -48,11 +49,7 @@ class HomePhotosDetailViewController : BaseViewController {
var datas : [AssetModel] = []
// 排序
var currentSort : Int = 0 {
didSet{
clearSelected()
}
}
var currentSort : Int = 0
// 当前页面选中的
var selectedModel : [AssetModel] = [] {
......@@ -117,6 +114,15 @@ class HomePhotosDetailViewController : BaseViewController {
}()
/// 过滤垃圾桶数据
/// - Parameter array: 当前数据
/// - Returns: 过滤后的数据
func filterTrashData(array : [AssetModel]) -> [AssetModel]{
let data = TrashDataManager.getCurrentMediaTypeTrashData(mediaType: self.mediaType == PhotsFileType.Other ? TrashTypeEnum.other : TrashTypeEnum.shot)
return array.filter({!data.map({$0.localIdentifier}).contains($0.localIdentifier)})
}
func setUI(){
......@@ -153,6 +159,28 @@ class HomePhotosDetailViewController : BaseViewController {
}
}
self.videoDetailNavView?.startSelectCallBack = {[weak self] choose in
guard let self else {return}
if !choose {
self.selectedModel.removeAll()
}
DispatchQueue.main.async {
UIView.transition(with: self.collectionView, duration: 0.3, options: .transitionCrossDissolve, animations: {
self.collectionView.reloadData()
}, completion: nil)
if self.selectedModel.count > 0 {
self.deleteButton.backgroundColor = UIColor(red: 0, green: 0.51, blue: 1, alpha: 1)
}else{
self.deleteButton.backgroundColor = UIColor(red: 0.7, green: 0.7, blue: 0.7, alpha: 1)
self.deleteButton.setTitle("Delete", for: .normal)
}
}
}
self.view.insertSubview(collectionView, at: 0)
......@@ -193,13 +221,13 @@ class HomePhotosDetailViewController : BaseViewController {
self.defaultImageView.isHidden = false
self.defaultTipLabel.isHidden = false
self.currentHeaderView?.btnView.isHidden = true
self.videoDetailNavView?.seletedAllBtn.isHidden = true
self.videoDetailNavView?.startSelectButton.isHidden = true
self.deleteButton.isHidden = true
}else{
self.defaultImageView.isHidden = true
self.defaultTipLabel.isHidden = true
self.currentHeaderView?.btnView.isHidden = false
self.videoDetailNavView?.seletedAllBtn.isHidden = false
self.videoDetailNavView?.startSelectButton.isHidden = false
self.deleteButton.isHidden = false
}
}
......@@ -303,24 +331,35 @@ extension HomePhotosDetailViewController:WaterfallMutiSectionDelegate,UICollecti
}else{
cell.choose = false
}
if let view = self.videoDetailNavView {
cell.selectImageView.isHidden = !view.startSelectButton.isSelected
cell.extensionView.isHidden = !view.startSelectButton.isSelected
}
cell.cellCallBack = {[weak self] ident,order in
guard let self else {return}
DispatchQueue.main.async {
// 点击之后跳转详情页面
let vc = PMShowImgVideoController()
vc.state = .similarPhotos
vc.currentIdx = 0
let dataSource = ImageSeletedCollectionItem()
dataSource.isSeleted = true
dataSource.id = ident
// 获取image
dataSource.image = PhotoAndVideoMananger.mananger.getImageFromAssetID(id: ident.localIdentifier)
vc.homeDataSource = [dataSource]
if self.mediaType == .screenshots {
let vc = PhotoRemoveViewController(data: self.resourceData,currentIndex: indexPath.row,mediaType: .shot)
vc.dismissCallback = {
self.dealData()
UIView.transition(with: self.collectionView, duration: 0.3, options: .transitionCrossDissolve, animations: {
self.collectionView.reloadData()
}, completion: nil)
}
self.navigationController?.pushViewController(vc, animated: true)
}
if self.mediaType == .Other {
let vc = PhotoRemoveViewController(data: self.resourceData,currentIndex: indexPath.row,mediaType: .other)
vc.dismissCallback = {
self.dealData()
UIView.transition(with: self.collectionView, duration: 0.3, options: .transitionCrossDissolve, animations: {
self.collectionView.reloadData()
}, completion: nil)
}
self.navigationController?.pushViewController(vc, animated: true)
}
}
}
cell.clickCallBack = {[weak self] click,order in
......@@ -655,9 +694,6 @@ extension HomePhotosDetailViewController:WaterfallMutiSectionDelegate,UICollecti
// 清理下缓存数据
PhotoDataManager.manager.removeDataWhenDeleteInPage(data: self.selectedModel)
// 更新页面
DispatchQueue.main.async {
// 删除完成之后,移除下当前选择的数据
......@@ -672,10 +708,4 @@ extension HomePhotosDetailViewController:WaterfallMutiSectionDelegate,UICollecti
setDefaultPage()
}
func clearSelected() {
self.selectedModel.removeAll()
}
}
......@@ -32,18 +32,15 @@ class HomeVideoDetailController :BaseViewController {
for item in self.model.assets {
dataArray = dataArray + item
}
self.resourceData = dataArray
self.resourceData = self.filterTrashData(array: dataArray)
self.selectedModel = self.filterTrashData(array: self.selectedModel)
self.sortByType(sortType: self.currentSort)
}
var model : HomePhotosModel
// 排序
var currentSort : Int = 0 {
didSet{
clearSelected()
}
}
var currentSort : Int = 0
// 当前页面选中的
var selectedModel : [AssetModel] = [] {
......@@ -112,6 +109,14 @@ class HomeVideoDetailController :BaseViewController {
return label
}()
/// 过滤垃圾桶数据
/// - Parameter array: 当前数据
/// - Returns: 过滤后的数据
func filterTrashData(array : [AssetModel]) -> [AssetModel]{
let data = TrashDataManager.getCurrentMediaTypeTrashData(mediaType: .video)
return array.filter({!data.map({$0.localIdentifier}).contains($0.localIdentifier)})
}
func setUI(){
videoDetailNavView = VideoDetaiNavView(frame: CGRect(x: 0, y: 0, width: self.view.width, height: statusBarHeight + 44))
......@@ -145,6 +150,27 @@ class HomeVideoDetailController :BaseViewController {
}
self.videoDetailNavView?.startSelectCallBack = {[weak self] choose in
guard let self else {return}
if !choose {
self.selectedModel.removeAll()
}
DispatchQueue.main.async {
UIView.transition(with: self.collectionView, duration: 0.3, options: .transitionCrossDissolve, animations: {
self.collectionView.reloadData()
}, completion: nil)
if self.selectedModel.count > 0 {
self.deleteButton.backgroundColor = UIColor(red: 0, green: 0.51, blue: 1, alpha: 1)
}else{
self.deleteButton.backgroundColor = UIColor(red: 0.7, green: 0.7, blue: 0.7, alpha: 1)
self.deleteButton.setTitle("Delete", for: .normal)
}
}
}
self.view.insertSubview(collectionView, at: 0)
......@@ -230,6 +256,11 @@ extension HomeVideoDetailController:WaterfallMutiSectionDelegate,UICollectionVie
cell.choose = false
}
if let view = self.videoDetailNavView {
cell.selectImageView.isHidden = !view.startSelectButton.isSelected
cell.extensionView.isHidden = !view.startSelectButton.isSelected
}
cell.clickCallBack = {[weak self] click,order in
guard let self else {return}
if click == true {
......@@ -253,32 +284,14 @@ extension HomeVideoDetailController:WaterfallMutiSectionDelegate,UICollectionVie
// 获取视频的图片
PhotoAndVideoMananger.mananger.getVideoImageByIdent(ident: ident) { image in
// 点击之后跳转详情页面
let vc = PMShowImgVideoController()
vc.state = .similarVideos
vc.currentIdx = 0
let dataSource = ImageSeletedCollectionItem()
dataSource.isSeleted = true
dataSource.id = ident
dataSource.image = image
// 表示这个是视频
vc.homeDataSource = [dataSource]
vc.getVideoURLFromLocalIdentifier(localIdentifier: ident.localIdentifier) { url, error in
if url != nil{
DispatchQueue.main.async {
vc.url = url
self.navigationController?.pushViewController(vc, animated: true)
}
}else{
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)
}
}
let vc = PhotoRemoveViewController(data: self.resourceData,currentIndex: indexPath.row,mediaType: .video)
vc.dismissCallback = {
self.dealData()
UIView.transition(with: self.collectionView, duration: 0.3, options: .transitionCrossDissolve, animations: {
self.collectionView.reloadData()
}, completion: nil)
}
self.navigationController?.pushViewController(vc, animated: true)
} errorHandler: {
DispatchQueue.main.async {
let alert = UIAlertController(title: nil, message: "Get Video image failure", preferredStyle: .alert)
......@@ -547,13 +560,13 @@ extension HomeVideoDetailController:WaterfallMutiSectionDelegate,UICollectionVie
self.defaultImageView.isHidden = false
self.defaultTipLabel.isHidden = false
self.currentHeaderView?.btnView.isHidden = true
self.videoDetailNavView?.seletedAllBtn.isHidden = true
self.videoDetailNavView?.startSelectButton.isHidden = true
self.deleteButton.isHidden = true
}else{
self.defaultImageView.isHidden = true
self.defaultTipLabel.isHidden = true
self.currentHeaderView?.btnView.isHidden = false
self.videoDetailNavView?.seletedAllBtn.isHidden = false
self.videoDetailNavView?.startSelectButton.isHidden = false
self.deleteButton.isHidden = false
}
}
......@@ -593,11 +606,6 @@ extension HomeVideoDetailController:WaterfallMutiSectionDelegate,UICollectionVie
}
func clearSelected() {
self.selectedModel.removeAll()
}
/// 改变标题
func changeHeaderTitle(){
var title : String = "Largest"
......
//
// PhotoDetailViewController.swift
// PhoneManager
//
// Created by edy on 2025/5/14.
//
import UIKit
class PhotoDetailViewController : BaseViewController {
lazy var showView : PMScaleImageView = {
let view = PMScaleImageView()
return view
}()
lazy var closeButton : UIButton = {
let view = UIButton(type: .custom)
view.setImage(UIImage(named: "ic_close_charging"), for: .normal)
view.addTarget(self, action: #selector(closeButtonAction), for: .touchUpInside)
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(self.closeButton)
self.closeButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(15)
make.top.equalToSuperview().offset(statusBarHeight + 8)
make.width.height.equalTo(28)
}
self.view.addSubview(self.showView)
self.showView.snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.top.equalTo(statusBarHeight + 44)
make.bottom.equalTo(-safeHeight)
}
}
@objc func closeButtonAction(){
self.dismiss(animated: true)
}
}
......@@ -21,13 +21,13 @@ class PhotoSlideViewController: BaseViewController {
private lazy var topView: AnchorRotatableView = {
let view = AnchorRotatableView()
view.configure(anchorPoint: CGPoint(x: 0.5, y: 0.5))
return view
}()
private lazy var bottomView: AnchorRotatableView = {
let view = AnchorRotatableView()
view.configure(anchorPoint: CGPoint(x: 0.5, y: 0.5))
return view
}()
......@@ -119,7 +119,8 @@ class PhotoSlideViewController: BaseViewController {
updateCardTransform(translation: translation)
updateButtons(progress: progress,translation: translation)
case .ended, .cancelled:
endDragAnimation(velocity: velocity)
break
// endDragAnimation(velocity: velocity)
default: break
}
}
......
......@@ -40,10 +40,3 @@ class AnchorRotatableView: UIView {
}
extension AnchorRotatableView {
func configure(anchorPoint: CGPoint) {
layer.anchorPoint = anchorPoint
}
}
......@@ -42,7 +42,7 @@ class HomeInfoView :UIView {
lazy var headerView:HomeInfoTitleView = {
let sview:HomeInfoTitleView = HomeInfoTitleView(frame: CGRect(x: 0, y: 0, width: width, height: 84))
sview.titleLabel.text = self.titleText
sview.filterButton.isHidden = self.type != .similar
sview.filterButton.isHidden = self.type != .similar && self.type != .SimilarVideos
tableView.addSubview(sview)
return sview
}()
......@@ -117,7 +117,13 @@ class HomeInfoView :UIView {
// 从源头获取相似数据
PhotoDataManager.manager.loadFromFileSystem(resultModel: {[weak self] model in
guard let self else {return}
let tempData = self.filterDataByDate(orgModels: model.titleModelArray[1].assets, startDate: filterModel.startDate, endDate: filterModel.endDate)
var dataS : [[AssetModel]] = []
if self.type == .SimilarVideos{
dataS = model.otherModelArray[3].assets
}else {
dataS = model.titleModelArray[1].assets
}
let tempData = self.filterDataByDate(orgModels: dataS , startDate: filterModel.startDate, endDate: filterModel.endDate)
// 重新更新下数据源
self.ids = self.sortData(source: tempData, type: filterModel.sortType)
var tempModels : [HomeInfoTableItem] = []
......@@ -307,6 +313,33 @@ extension HomeInfoView:UITableViewDataSource,UITableViewDelegate {
self.tableScrollToNextRow()
}
// 重新设置models
func resetModels(){
var newArray : [HomeInfoTableItem] = []
for array in ids ?? [] {
var smodels:[ImageSeletedCollectionItem] = []
for id in array {
let smodel = ImageSeletedCollectionItem()
smodel.id = id
smodel.isSeleted = false
smodels.append(smodel)
}
let smodel = HomeInfoTableItem()
smodel.type = type
smodel.smodels = smodels
smodel.titleText = titleText
newArray.append(smodel)
}
self.models = newArray
}
/// 让表格自动滚动一行
private func tableScrollToNextRow(){
......@@ -345,6 +378,11 @@ extension HomeInfoView:UITableViewDataSource,UITableViewDelegate {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: HomeInfoTableViewCell.identifier, for: indexPath) as! HomeInfoTableViewCell
cell.saveKeepListFinishedCallback = {
self.ids?.remove(at: indexPath.section)
self.resetModels()
tableView.deleteRows(at: [indexPath], with: .automatic)
}
cell.type = self.type
cell.model = models[indexPath.row]
......@@ -492,7 +530,7 @@ class HomeInfoTitleView:UIView {
self.filterButton.snp.makeConstraints { make in
make.top.equalToSuperview().offset(35)
make.centerY.equalTo(self.titleLabel.snp.centerY)
make.right.equalToSuperview().offset(-15)
make.height.equalTo(28)
make.width.equalTo(98)
......
//
// PhotoRemoveNavView.swift
// PhoneManager
//
// Created by edy on 2025/5/13.
//
import UIKit
import SnapKit
class PhotoRemoveNavView: UIView {
public var backButton:UIButton!
public var sizeLbel : UILabel!
public var titleLbel : UILabel!
public var resetButton :UIButton!
private var topConstraint: Constraint?
var reSetCallBack:()->Void = {}
public var mediaType : TrashTypeEnum = .shot {
didSet{
if mediaType == .video {
self.sizeLbel.isHidden = false
self.topConstraint?.update(offset: 22 + statusBarHeight)
}else{
self.sizeLbel.isHidden = true
self.topConstraint?.update(offset: 6 + statusBarHeight)
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupUI() {
backgroundColor = .white
backButton = UIButton()
backButton.setImage(UIImage(named: "nav_back"), for: .normal)
backButton.addTarget(self, action: #selector(backBtnClick), for: .touchUpInside)
self.addSubview(backButton)
backButton.snp.makeConstraints { make in
make.centerY.equalTo(navCenterY)
make.left.equalToSuperview().offset(marginLR)
make.width.height.equalTo(iconWH)
}
sizeLbel = UILabel()
sizeLbel.textColor = UIColor(red: 0.07, green: 0.07, blue: 0.07, alpha: 1)
sizeLbel.font = UIFont.systemFont(ofSize: 12, weight: .medium)
self.addSubview(sizeLbel)
sizeLbel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(statusBarHeight)
make.height.equalTo(22)
}
titleLbel = UILabel()
titleLbel.textColor = UIColor(red: 0.07, green: 0.07, blue: 0.07, alpha: 1)
titleLbel.font = UIFont.systemFont(ofSize: 12, weight: .medium)
self.addSubview(titleLbel)
titleLbel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
self.topConstraint = make.top.equalToSuperview().offset(6 + statusBarHeight).constraint
make.height.equalTo(22)
}
resetButton = UIButton()
resetButton.setImage(UIImage(named: "Frame 3"), for: .normal)
resetButton.addTarget(self, action: #selector(resetButtonAction), for: .touchUpInside)
resetButton.isHidden = true
self.addSubview(resetButton)
resetButton.snp.makeConstraints { make in
make.centerY.equalTo(navCenterY)
make.right.equalToSuperview().offset(-marginLR)
make.width.height.equalTo(iconWH)
}
}
@objc private func backBtnClick() {
self.responderViewController()?.navigationController?.popViewController(animated: true)
}
@objc private func resetButtonAction() {
self.reSetCallBack()
}
}
//
// AnchorRotatableView.swift
// AIClean
//
// Created by 赵前 on 2025/5/10.
//
import Foundation
import UIKit
class PhotosRemoveBaseView: UIView {
var currentIndex : Int?
var initialCenter: CGPoint = .zero
var url : URL?
var innerVideoController : VideoViewController?
func reload(index:Int) -> Void {
if let url = url {
self.innerVideoController = VideoViewController()
if let vc = self.innerVideoController {
vc.isAutoPlay = index == 0
vc.url = url
self.imageView.removeFromSuperview()
self.addSubview(vc.view)
vc.view.snp.makeConstraints { make in
make.left.top.bottom.right.equalToSuperview()
}
}
}
}
var mediaType : TrashTypeEnum?
lazy var maskTempleteView : UIView = {
let view = UIView(frame: CGRectMake(0, 0, self.width, self.height))
return view
}()
private let imageView = UIImageView()
private let leftButton = UIButton()
private let rightButton = UIButton()
override init(frame: CGRect) {
super.init(frame: frame)
self.layer.cornerRadius = 20
self.clipsToBounds = true
setupUI()
self.imageView.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer()
tap.addTarget(self, action: #selector(showDeatail))
self.imageView.addGestureRecognizer(tap)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupUI() {
// 配置图片视图
imageView.contentMode = .scaleAspectFit
addSubview(imageView)
leftButton.frame = CGRect(x: self.width / 2 - 45.5, y: self.height / 2 - 21, width: 95, height: 42)
leftButton.setTitle("Delete", for: .normal)
leftButton.backgroundColor = UIColor(red: 0.95, green: 0.21, blue: 0.21, alpha: 1)
leftButton.tintColor = .white
leftButton.layer.cornerRadius = 21
leftButton.clipsToBounds = true
leftButton.alpha = 0
addSubview(leftButton)
rightButton.frame = CGRect(x: self.width / 2 - 45.5, y: self.height / 2 - 21, width: 95, height: 42)
rightButton.setTitle("Retain", for: .normal)
rightButton.backgroundColor = UIColor(red: 0.18, green: 0.76, blue: 0.35, alpha: 1)
rightButton.tintColor = .white
rightButton.layer.cornerRadius = 21
rightButton.clipsToBounds = true
rightButton.alpha = 0
addSubview(rightButton)
}
func configure(with image: UIImage) {
imageView.image = image
imageView.frame = bounds
}
func showLeftButton() {
self.leftButton.center = self.center
UIView.animate(withDuration: 0.2) {
self.leftButton.alpha = 1
self.maskTempleteView.backgroundColor = UIColor(red: 0.95, green: 0.21, blue: 0.21, alpha: 0.4000)
self.addSubview(self.maskTempleteView)
self.rightButton.alpha = 0
}
}
func showRightButton() {
self.rightButton.center = self.center
UIView.animate(withDuration: 0.2) {
self.rightButton.alpha = 1
self.maskTempleteView.backgroundColor = UIColor(red: 0.26, green: 0.78, blue: 0.41, alpha: 0.4000)
self.addSubview(self.maskTempleteView)
self.leftButton.alpha = 0
}
}
func hideButtons() {
UIView.animate(withDuration: 0.2) {
self.leftButton.alpha = 0
self.rightButton.alpha = 0
self.maskTempleteView.removeFromSuperview()
self.leftButton.center = self.center
self.rightButton.center = self.center
}
}
@objc func showDeatail(){
let vc : PhotoDetailViewController = PhotoDetailViewController()
vc.showView.icon = self.imageView.image
vc.modalPresentationStyle = .fullScreen
self.responderViewController()?.present(vc, animated: true)
}
}
//
// TrashSubView.swift
// PhoneManager
//
// Created by edy on 2025/5/13.
//
import UIKit
class TrashSubView: UIView {
var clearTashDataCallBack : ()->Void = {}
var presentTashDetailViewClickCallBack : ()->Void = {}
lazy var resourceCountlabel : UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 24, weight: .semibold)
label.textColor = UIColor(red: 0, green: 0, blue: 0, alpha: 1)
return label
}()
lazy var tipLabel : UILabel = {
let label = UILabel()
label.text = "The photo in the trash can"
label.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
label.textColor = UIColor(red: 0, green: 0, blue: 0, alpha: 1)
label.numberOfLines = 0
return label
}()
lazy var emptyButton: UIButton = {
let button = UIButton(type: .custom)
button.setImage(UIImage(named: "ic_delete_duplicates"), for: .normal)
button.backgroundColor = UIColor(red: 0, green: 0.51, blue: 1, alpha: 1)
button.setTitle("Empty the garbage", for: .normal)
button.setTitleColor(.white, for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
button.layer.cornerRadius = 19
button.clipsToBounds = true
button.addTarget(self, action: #selector(emptyButtonAction), for: .touchUpInside)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
// 给当前视图添加手势
addTapGestureToSelf()
// 添加UI
addUIViews()
setUILocation()
}
private func addUIViews(){
self.addSubview(self.resourceCountlabel)
self.addSubview(self.tipLabel)
self.addSubview(self.emptyButton)
}
private func setUILocation(){
self.resourceCountlabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.centerY.equalToSuperview()
make.height.equalTo(22)
make.width.equalTo(30)
}
self.tipLabel.snp.makeConstraints { make in
make.top.equalToSuperview().offset(17)
make.bottom.equalToSuperview().offset(-17)
make.left.equalTo(self.resourceCountlabel.snp.right).offset(11)
make.width.equalTo(100)
}
self.emptyButton.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-9)
make.height.equalTo(38)
make.centerY.equalToSuperview()
make.width.equalTo(145)
}
}
private func addTapGestureToSelf(){
self.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer()
tap.addTarget(self, action: #selector(tapHandle))
self.addGestureRecognizer(tap)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension TrashSubView {
@objc func emptyButtonAction(){
self.clearTashDataCallBack()
}
@objc func tapHandle(){
self.presentTashDetailViewClickCallBack()
}
}
......@@ -13,6 +13,22 @@ class VideoDetaiNavView : UIView {
private var backButton:UIButton!
var startSelectCallBack : (Bool)->Void = {choose in}
lazy var startSelectButton:UIButton = {
let button :UIButton = UIButton()
button.setTitle("Select", for: .normal)
button.setTitle("Cancel", for: .selected)
button.setTitleColor(UIColor(red: 0, green: 0.51, blue: 1, alpha: 1), for: .normal)
button.backgroundColor = UIColor(red: 0.95, green: 0.96, blue: 0.98, alpha: 1)
button.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .semibold)
button.layer.cornerRadius = 12
button.clipsToBounds = true
button.addTarget(self, action: #selector(startSelectButtonAction), for: .touchUpInside)
return button
}()
lazy var seletedAllBtn:UIButton = {
let btn:UIButton = UIButton(frame: CGRect(x: 0, y: 0, width: 115, height: 32))
......@@ -53,20 +69,23 @@ class VideoDetaiNavView : UIView {
self.addSubview(self.seletedAllBtn)
seletedAllBtn.x = self.width - marginLR - seletedAllBtn.width
seletedAllBtn.x = marginLR
seletedAllBtn.centerY = navCenterY
// self.seletedAllBtn.snp.makeConstraints { make in
// make.right.equalToSuperview().offset(-23)
// make.centerY.equalTo(self.backButton.snp.centerY)
// }
backButton.snp.makeConstraints { make in
make.centerY.equalTo(navCenterY)
make.left.equalToSuperview().offset(marginLR)
make.width.height.equalTo(iconWH)
}
self.addSubview(self.startSelectButton)
self.startSelectButton.snp.makeConstraints { make in
make.centerY.equalTo(self.backButton.snp.centerY)
make.right.equalToSuperview().offset(-marginLR)
make.width.equalTo(91)
make.height.equalTo(24)
}
self.seletedAllBtn.isHidden = true
}
......@@ -78,12 +97,21 @@ class VideoDetaiNavView : UIView {
DispatchQueue.main.async {[weak self] in
guard let self else {return}
// seletedAllBtn.isSelected = !seletedAllBtn.isSelected
seletedAllBtn.isSelected = !seletedAllBtn.isSelected
self.seletedAllBtn.width = seletedAllBtn.isSelected ? 131 : 115
seletedAllBtn.x = self.width - marginLR - seletedAllBtn.width
seletedAllBtn.x = marginLR
self.selectAllCallBack(seletedAllBtn.isSelected)
}
}
@objc func startSelectButtonAction(){
self.startSelectButton.isSelected = !self.startSelectButton.isSelected
self.seletedAllBtn.isHidden = !self.startSelectButton.isSelected
if !self.startSelectButton.isSelected{
seletedAllBtn.isSelected = true
seletedAllBtnClick()
}
self.startSelectCallBack(self.startSelectButton.isSelected)
}
}
......@@ -32,7 +32,7 @@ class YearMonthPickerView: UIView {
return picker
}()
private let years = Array(Array(Calendar.current.component(.year, from: Date())-30...Calendar.current.component(.year, from: Date())).reversed())
private let years = Array(Array(1970...Calendar.current.component(.year, from: Date())).reversed())
private lazy var months: [String] = {
let formatter = DateFormatter()
......
......@@ -24,6 +24,8 @@ class HomeInfoTableViewCell:UITableViewCell {
var callBack:callBack<Any> = {text in}
var saveKeepListFinishedCallback : ()->Void = {}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
......@@ -217,10 +219,27 @@ extension HomeInfoTableViewCell:UICollectionViewDelegate,UICollectionViewDataSou
// 显示保留按钮或者最佳匹配结果按钮
cell.allKeepButton.isHidden = indexPath.item != 0 || self.type == .duplicates
cell.bestResultButton.isHidden = indexPath.item != 0 || self.type == .duplicates
cell.model = model?.smodels?[indexPath.row]
cell.photsFileType = model?.type
cell.keepAllCallBack = {
//存到保留列表
saveAllDataToKeepList()
self.saveKeepListFinishedCallback()
}
// 存全部数据到保留列表
func saveAllDataToKeepList(){
let uniqueId = UUID().uuidString
if let tempModel = self.model?.smodels {
for item in tempModel {
if let assetModel = item.id {
let success = GroupDatabase.shared.insert(localIdentifier: assetModel.localIdentifier, assetSize: assetModel.assetSize, createDate: assetModel.createDate, mediaType: self.type == .SimilarVideos ? 2 : 1 , groupId: uniqueId)
if success == false {
Print("存入当前数据到保留列表失败")
}
}
}
}
}
cell.callBack = {[weak self] _ in
......@@ -253,9 +272,22 @@ extension HomeInfoTableViewCell:UICollectionViewDelegate,UICollectionViewDataSou
}else{
vc.state = .similarPhotos
}
vc.oldPageIndexPath = indexPath
vc.currentIdx = indexPath.row
vc.homeDataSource = self.model?.smodels
self.responderViewController()?.navigationController?.pushViewController(vc, animated: true)
vc.backOrgPageCallBack = {[weak self]index,data in
guard let self else {return}
self.model?.smodels = data
DispatchQueue.main.async{
if let view = self.collectionView{
UIView.transition(with: view, duration: 0.3, options: .transitionCrossDissolve, animations: {
view.reloadData()
}, completion: nil)
}
}
}
}
}
......
......@@ -80,14 +80,12 @@ class HomeVideoDetailCell : UICollectionViewCell {
lazy var backImageView: UIImageView = {
let view = UIImageView()
view.isUserInteractionEnabled = true
view.contentMode = .scaleAspectFill
view.clipsToBounds = true
view.layer.masksToBounds = true
view.layer.cornerRadius = 12
view.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer()
tap.addTarget(self, action: #selector(cellClick))
view.addGestureRecognizer(tap)
......
......@@ -19,6 +19,8 @@ class ImageSeletedCollectionCell:UICollectionViewCell {
var callBack:callBack<Any> = {text in}
var keepAllCallBack : ()->Void = {}
lazy var allKeepButton : UIButton = {
let button = UIButton(type: .custom)
......@@ -37,8 +39,8 @@ class ImageSeletedCollectionCell:UICollectionViewCell {
let button = UIButton(type: .custom)
button.layer.cornerRadius = 8
button.clipsToBounds = true
button.backgroundColor = UIColor(red: 0, green: 0.51, blue: 1, alpha: 1)
button.setTitle( "Best result", for: .normal)
button.backgroundColor = UIColor(red: 1, green: 0.65, blue: 0, alpha: 1)
button.setTitle( "Best", for: .normal)
button.setImage(UIImage(named: "Frame"), for: .normal)
button.setTitleColor(.white, for: .normal)
button.isHidden = true
......@@ -179,7 +181,7 @@ class ImageSeletedCollectionCell:UICollectionViewCell {
self.bestResultButton.snp.makeConstraints { make in
make.left.bottom.equalToSuperview()
make.width.equalTo(81)
make.width.equalTo(52)
make.height.equalTo(16)
}
......@@ -222,6 +224,8 @@ class ImageSeletedCollectionCell:UICollectionViewCell {
extension ImageSeletedCollectionCell {
@objc func allKeepButtonAction(){
Print("点击了全部保留按钮")
self.keepAllCallBack()
}
}
......@@ -29,6 +29,21 @@ class PMShowImgCell: UICollectionViewCell {
}
}
lazy var bestResultButton:UIButton = {
let button = UIButton(type: .custom)
button.setImage(UIImage(named: "Frame"), for: .normal)
button.setTitle("Best", for: .normal)
button.setTitleColor(.white, for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .semibold)
button.layer.cornerRadius = 10.5
button.clipsToBounds = true
button.backgroundColor = UIColor(red: 1, green: 0.65, blue: 0, alpha: 1)
contentView.addSubview(button)
return button
}()
private lazy var iconView: UIImageView = {
let info = UIImageView()
info.contentMode = .scaleAspectFill
......@@ -63,6 +78,14 @@ class PMShowImgCell: UICollectionViewCell {
}
private func setup() -> Void {
bestResultButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(15)
make.top.equalToSuperview().offset(34)
make.width.equalTo(63)
make.height.equalTo(21)
}
iconView.snp.makeConstraints { make in
make.left.right.bottom.top.equalToSuperview()
}
......@@ -74,5 +97,7 @@ class PMShowImgCell: UICollectionViewCell {
make.width.height.equalTo(iconWH)
make.bottom.equalToSuperview().offset(-10)
}
self.contentView.bringSubviewToFront(self.bestResultButton)
}
}
......@@ -17,6 +17,8 @@ class PMShowVideoCell: UICollectionViewCell {
var type : Int = 0
var innerVideoController : VideoViewController?
func reload() -> Void {
var url : URL?
if type == 0 {
......@@ -25,25 +27,39 @@ class PMShowVideoCell: UICollectionViewCell {
url = homeResouceUrl
}
if let url = url {
player.playVideo(from: url)
self.innerVideoController = VideoViewController()
if let vc = self.innerVideoController {
vc.url = url
self.contentView.addSubview(vc.view)
self.contentView.bringSubviewToFront(self.selectBtn)
vc.view.snp.makeConstraints { make in
make.left.top.bottom.right.equalToSuperview()
}
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
self.contentView.addSubview(self.selectBtn)
selectBtn.snp.makeConstraints { make in
make.right.equalToSuperview()
make.width.height.equalTo(iconWH)
make.bottom.equalToSuperview().offset(-10)
}
bestResultButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(15)
make.top.equalToSuperview().offset(34)
make.width.equalTo(63)
make.height.equalTo(21)
}
self.contentView.bringSubviewToFront(self.bestResultButton)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setup() -> Void {
player.snp.makeConstraints { make in
make.left.top.bottom.right.equalToSuperview()
}
}
var zPlayer : SecretVideoPlayer {
get {
return player
......@@ -57,4 +73,44 @@ class PMShowVideoCell: UICollectionViewCell {
contentView.addSubview(p)
return p
}()
var callblock:(()->Void) = {}
@objc private func selectTap() -> Void {
callblock()
}
var isCurrent:Bool = false {
didSet {
selectBtn.isSelected = isCurrent
}
}
lazy var selectBtn: UIButton = {
let select = UIButton(type: .custom)
select.setImage(UIImage(named: "home_info_norl"), for: .normal)
select.setImage(UIImage(named: "home_info_seleted"), for: .selected)
select.addTarget(self, action: #selector(selectTap), for: .touchUpInside)
contentView.addSubview(select)
return select
}()
lazy var bestResultButton:UIButton = {
let button = UIButton(type: .custom)
button.setImage(UIImage(named: "Frame"), for: .normal)
button.setTitle("Best", for: .normal)
button.setTitleColor(.white, for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .semibold)
button.layer.cornerRadius = 10.5
button.clipsToBounds = true
button.backgroundColor = UIColor(red: 1, green: 0.65, blue: 0, alpha: 1)
contentView.addSubview(button)
return button
}()
}
......@@ -21,16 +21,32 @@ class PMShowImgVideoController: BaseViewController {
// 资源类型 0-图片 1-视频
var homeMediaType : Int = 0
// 记录原来进来的
var oldPageIndexPath : IndexPath?
var state:ShowState = .secret
var imageVideoPath:[String] = []
// 首页过来的数据
var homeDataSource : [ImageSeletedCollectionItem]?
var homeDataSource : [ImageSeletedCollectionItem]? {
didSet{
if let data = homeDataSource {
for (index,item) in data.enumerated() {
if item.isSeleted == true {
selectSet.add(index)
}
}
}
}
}
var currentIdx = 0
var selectSet = NSMutableSet()
var backOrgPageCallBack : (IndexPath?,[ImageSeletedCollectionItem]?)->Void = {index,data in}
var url : URL?
override func viewDidLoad() {
......@@ -53,6 +69,19 @@ class PMShowImgVideoController: BaseViewController {
self.MaxCollection.scrollToItem(at: IndexPath(row: currentIdx, section: 0), at: .centeredHorizontally, animated: false)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// 离开返回上一个把选择的数据带过去
for item in self.selectSet {
if let index = item as? Int {
self.homeDataSource?[index].isSeleted = true
}
}
self.backOrgPageCallBack(self.oldPageIndexPath,self.homeDataSource)
}
private lazy var MaxCollection: UICollectionView = {
let flowlayout = UICollectionViewFlowLayout()
flowlayout.sectionInset = UIEdgeInsets()
......@@ -131,8 +160,10 @@ extension PMShowImgVideoController : UICollectionViewDelegate,UICollectionViewDa
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if collectionView == bottItems {
if selectSet.contains(indexPath.row) {
self.homeDataSource?[indexPath.row].isSeleted = false
selectSet.remove(indexPath.row)
}else{
self.homeDataSource?[indexPath.row].isSeleted = true
selectSet.add(indexPath.row)
}
self.MaxCollection.reloadData()
......@@ -158,7 +189,7 @@ extension PMShowImgVideoController : UICollectionViewDelegate,UICollectionViewDa
let showName = imageVideoPath[indexPath.row]
if !showName.hasSuffix(".png") {
guard let zCell:PMShowVideoCell = cell as? PMShowVideoCell else { return }
zCell.zPlayer.pause()
zCell.innerVideoController?.playerViewController.player?.pause()
}
}
}
......@@ -172,12 +203,16 @@ extension PMShowImgVideoController : UICollectionViewDelegate,UICollectionViewDa
showName.loadPhotoOrVideo { durs, icon in
cell.icon = icon
}
cell.bestResultButton.isHidden = indexPath.row != 0
cell.isCurrent = selectSet.contains(indexPath.row)
cell.callblock = {[weak self] in
guard let self = self else { return }
if self.selectSet.contains(indexPath.row){
self.homeDataSource?[indexPath.row].isSeleted = false
self.selectSet.remove(indexPath.row)
}else{
self.homeDataSource?[indexPath.row].isSeleted = true
self.selectSet.add(indexPath.row)
}
self.MaxCollection.reloadData()
......@@ -186,8 +221,21 @@ extension PMShowImgVideoController : UICollectionViewDelegate,UICollectionViewDa
return cell
}else{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PMShowVideoCellID, for: indexPath) as! PMShowVideoCell
cell.isCurrent = selectSet.contains(indexPath.row)
cell.playURL = showName
cell.reload()
cell.callblock = {[weak self] in
guard let self = self else { return }
if self.selectSet.contains(indexPath.row){
self.homeDataSource?[indexPath.row].isSeleted = false
self.selectSet.remove(indexPath.row)
}else{
self.homeDataSource?[indexPath.row].isSeleted = true
self.selectSet.add(indexPath.row)
}
self.MaxCollection.reloadData()
self.bottItems.reloadData()
}
return cell
}
}else{
......@@ -208,11 +256,14 @@ extension PMShowImgVideoController : UICollectionViewDelegate,UICollectionViewDa
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PMShowImgCellID, for: indexPath) as! PMShowImgCell
cell.icon = self.homeDataSource![indexPath.row].image ?? UIImage()
cell.isCurrent = selectSet.contains(indexPath.row)
cell.bestResultButton.isHidden = indexPath.row != 0
cell.callblock = {[weak self] in
guard let self = self else { return }
if self.selectSet.contains(indexPath.row){
self.homeDataSource?[indexPath.row].isSeleted = false
self.selectSet.remove(indexPath.row)
}else{
self.homeDataSource?[indexPath.row].isSeleted = true
self.selectSet.add(indexPath.row)
}
self.MaxCollection.reloadData()
......@@ -247,6 +298,20 @@ extension PMShowImgVideoController : UICollectionViewDelegate,UICollectionViewDa
}
// 表示这个是视频
cell.type = 1
cell.isCurrent = selectSet.contains(indexPath.row)
cell.bestResultButton.isHidden = indexPath.row != 0
cell.callblock = {[weak self] in
guard let self = self else { return }
if self.selectSet.contains(indexPath.row){
self.homeDataSource?[indexPath.row].isSeleted = false
self.selectSet.remove(indexPath.row)
}else{
self.homeDataSource?[indexPath.row].isSeleted = true
self.selectSet.add(indexPath.row)
}
self.MaxCollection.reloadData()
self.bottItems.reloadData()
}
return cell
}else{
if indexPath.row < self.homeDataSource?.count ?? 0 {
......
......@@ -329,6 +329,16 @@ extension SecretViewController : UICollectionViewDelegate,UICollectionViewDataSo
let play = PMShowImgVideoController()
play.currentIdx = indexPath.row
play.imageVideoPath = dataSource
play.oldPageIndexPath = indexPath
play.backOrgPageCallBack = {[weak self]index,data in
guard let self else {return}
self.selectArray = play.selectSet
DispatchQueue.main.async{
UIView.transition(with: self.secretCollect, duration: 0.3, options: .transitionCrossDissolve, animations: {
self.secretCollect.reloadData()
}, completion: nil)
}
}
self.navigationController?.pushViewController(play, animated: true)
}
......
......@@ -11,6 +11,12 @@ import AVFoundation
class SecretVideoPlayer: UIView {
var callBackCurrtntTimeString : (String)->Void = {currentTimeString in }
var tapPauseCallback : (playState)->Void = {state in}
var timeObserverToken: Any?
private var player: AVPlayer?
var isLooping = false
......@@ -48,11 +54,11 @@ class SecretVideoPlayer: UIView {
state = .playing
player?.seek(to: CMTime.zero)
resume()
}
else{
}else{
state = .pause
pause()
}
self.tapPauseCallback(state)
}
private func setupPlayerLayer() {
......@@ -91,15 +97,31 @@ class SecretVideoPlayer: UIView {
}
NotificationCenter.default.addObserver(
self,
selector: #selector(playerItemDidReachEnd),
selector: #selector( playerItemDidReachEnd),
name: .AVPlayerItemDidPlayToEndTime,
object: player?.currentItem
)
addTimeObserver()
state = .playing
player?.play()
}
func addTimeObserver(){
let interval = CMTime(seconds: 0.1, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
timeObserverToken = player?.addPeriodicTimeObserver(forInterval: interval, queue: .main) { [weak self] time in
guard let self = self, let currentItem = self.player?.currentItem else { return }
let currentTime = time.seconds
let totalDuration = currentItem.duration.seconds
// 更新进度标签
self.callBackCurrtntTimeString("\(self.formatTime(seconds: currentTime))")
}
}
// 播放完成回调
@objc private func playerItemDidReachEnd(notification: Notification) {
if isLooping {
......@@ -107,6 +129,7 @@ class SecretVideoPlayer: UIView {
player?.play()
}
state = .end
self.tapPauseCallback(state)
}
// 暂停播放
......@@ -128,10 +151,30 @@ class SecretVideoPlayer: UIView {
name: .AVPlayerItemDidPlayToEndTime,
object: nil
)
if let token = timeObserverToken {
self.player?.removeTimeObserver(token)
timeObserverToken = nil
}
}
// 清理资源
deinit {
removeObservers()
}
func formatTime(seconds: Double) -> String {
guard !seconds.isNaN else { return "00:00" }
let totalSeconds = Int(seconds)
let hours = totalSeconds / 3600
let minutes = (totalSeconds % 3600) / 60
let seconds = totalSeconds % 60
if hours > 0 {
return String(format: "%02d:%02d:%02d", hours, minutes, seconds)
} else {
return String(format: "%02d:%02d", minutes, seconds)
}
}
}
......@@ -7,7 +7,9 @@
import UIKit
class TrashViewController: UIViewController {
class TrashViewController: BaseViewController {
var dissmisCallBack:()->Void = {}
var source:[TrashTypeEnum] = [.video,.other,.shot,.chat]
......@@ -17,11 +19,42 @@ class TrashViewController: UIViewController {
var currentType:TrashTypeEnum = .other
let pageCount = 4 // 总页数
var currentPage = 1
var currentPage = 1 {
didSet{
self.setDelButtonUI()
}
}
override func viewDidLoad() {
super.viewDidLoad()
configUI()
getTrashDataAndRefreshPage()
setContentOffSet()
}
/// 获取垃圾桶数据
private func getTrashDataAndRefreshPage(){
for item in self.source {
let data = TrashDataManager.getCurrentMediaTypeTrashData(mediaType: item)
for view in self.contentScrollView.subviews {
if view.isKind(of: TrashContenView.self){
let tempView = view as! TrashContenView
if tempView.trashType == item{
tempView.mediaType = item
tempView.dataSource = data
}
}
}
}
// 将所有数据获取完成之后,重新刷新下删除按钮
setDelButtonUI()
}
// 在第一次进去的时候执行一次
private func setContentOffSet(){
self.contentScrollView.contentOffset = CGPointMake(ScreenW * CGFloat(self.currentPage - 1), 0)
}
......@@ -42,7 +75,8 @@ class TrashViewController: UIViewController {
delBtn.setTitleColor(.white, for: .normal)
delBtn.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .semibold)
delBtn.backgroundColor = UIColor.colorWithHex(hexStr: "#0082FF")
delBtn.layer.cornerRadius = 10
delBtn.layer.cornerRadius = 20
delBtn.addTarget(self, action: #selector(delBtnAction), for: .touchUpInside)
view.addSubview(delBtn)
delBtn.snp.makeConstraints { make in
......@@ -65,6 +99,12 @@ class TrashViewController: UIViewController {
contentScrollView.addSubview(otherView)
contentScrollView.addSubview(shotView)
contentScrollView.addSubview(chatView)
videoView.deleteButton = delBtn
otherView.deleteButton = delBtn
shotView.deleteButton = delBtn
chatView.deleteButton = delBtn
}
override func viewWillLayoutSubviews() {
......@@ -171,4 +211,64 @@ extension TrashViewController:UIScrollViewDelegate{
self.currentPage = currentPage + 1
}
@objc func delBtnAction(){
let info = self.getTypeByCurrentPage(pageIndex: currentPage)
let data = TrashDataManager.getCurrentMediaTypeTrashData(mediaType: info.0)
if data.count > 0 {
TrashDataManager.clearTrashData(mediaType: info.0) {
info.1.dataSource.removeAll()
}
}
}
private func getTypeByCurrentPage(pageIndex : Int)->(TrashTypeEnum,TrashContenView){
if self.currentPage == 1 {
return (TrashTypeEnum.video,self.videoView)
}
if self.currentPage == 2 {
return (TrashTypeEnum.other,self.otherView)
}
if self.currentPage == 3 {
return (TrashTypeEnum.shot,self.shotView)
}
if self.currentPage == 4 {
return (TrashTypeEnum.chat,self.chatView)
}
return (TrashTypeEnum.video,self.videoView)
}
private func setDelButtonUI(){
let info = self.getTypeByCurrentPage(pageIndex: currentPage)
let data : [AssetModel] = TrashDataManager.getCurrentMediaTypeTrashData(mediaType: info.0)
let type : TrashTypeEnum = info.0
DispatchQueue.main.async {
if data.count <= 0{
self.delBtn.setTitle("Delete", for: .normal)
self.delBtn.backgroundColor = UIColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1)
}else {
self.delBtn.backgroundColor = UIColor(red: 0, green: 0.51, blue: 1, alpha: 1)
if type == .video{
self.delBtn.setTitle("Delete \(data.count) \(self.getUnits(type:type,data:data))", for: .normal)
}else {
self.delBtn.setTitle("Delete \(data.count) \(self.getUnits(type:type,data:data))", for: .normal)
}
}
}
}
// 获取单位复数
private func getUnits(type:TrashTypeEnum,data: [AssetModel])->String{
if data.count > 1 {
return type == .video ? "videos" : "photos"
}else {
return type == .video ? "video" : "photo"
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.dissmisCallBack()
}
}
//
// TrashDataManager.swift
// PhoneManager
//
// Created by edy on 2025/5/14.
//
import UIKit
class TrashDataManager {
// 撤销当前类型垃圾桶某个数据
static func revokeTrashData(mediaType: TrashTypeEnum?, identifier:String, comlete:@escaping ()->Void){
// 清空单利和数据库的数据
clearCurrentMediaTypeTrashSigtonData(mediaType: mediaType,identifier: identifier)
clearCurrentMediaTypeTrashDBData(mediaType: mediaType,identifier: identifier)
comlete()
}
/// 清除当前类型垃圾桶的所有数据
static func clearTrashData(mediaType: TrashTypeEnum?, comlete:@escaping ()->Void){
let trashData = self.getCurrentMediaTypeTrashData(mediaType: mediaType)
if trashData.count > 0 {
PhotoAndVideoMananger.deleteAssets(localIdentifiers: trashData.map({$0.localIdentifier})) {
// 清空单利和数据库的数据
clearCurrentMediaTypeTrashSigtonData(mediaType: mediaType)
clearCurrentMediaTypeTrashDBData(mediaType: mediaType)
// 删除完成之后回调
comlete()
}
}else{
comlete()
}
}
static func clearCurrentMediaTypeTrashSigtonData(mediaType:TrashTypeEnum?,identifier:String? = nil){
if let type = mediaType {
if identifier == nil{
Singleton.shared.trashData[type] = []
}else {
Singleton.shared.trashData[type] = Singleton.shared.trashData[type]?.filter({$0.localIdentifier != identifier})
}
Print("删除单利中当前垃圾桶数据成功")
}
}
static func clearCurrentMediaTypeTrashDBData(mediaType:TrashTypeEnum?,identifier:String? = nil){
if let type = mediaType {
if identifier == nil{
let dataDB = TrashDatabase.shared.queryByMediaType(type.dbType)
let success = TrashDatabase.shared.batchDelete(localIdentifiers: dataDB.map({$0.localIdentifier}))
if !success {
Print("删除数据库当前垃圾桶数据失败")
}
}else{
let success = TrashDatabase.shared.delete(localIdentifier: identifier!)
if !success {
Print("删除数据库当前数据失败")
}
}
}
}
static func getCurrentMediaTypeTrashData(mediaType: TrashTypeEnum?) -> [AssetModel]{
var assetModel : [AssetModel] = []
if let type = mediaType{
// 从数据库拿数据
let dataDB = TrashDatabase.shared.queryByMediaType(type.dbType)
for item in dataDB {
assetModel.append(AssetModel.init(localIdentifier: item.localIdentifier, assetSize: item.assetSize, createDate: item.createDate, mediaType: item.mediaType))
}
// 从单利拿数据
if let type = mediaType{
if let dataSg = Singleton.shared.trashData[type]{
assetModel = assetModel + dataSg
}
}
}
return assetModel
}
}
......@@ -7,8 +7,11 @@
import Foundation
enum TrashTypeEnum{
case video,other,shot,chat
enum TrashTypeEnum : String, CaseIterable{
case video = "Video"
case other = "Other"
case shot = "Screenshot"
case chat = "Chat"
var dbType:Int{
switch self {
......
......@@ -9,6 +9,10 @@ import UIKit
class TrashContenAssetCell: UICollectionViewCell {
var mediaType : TrashTypeEnum?
var revokeCallBack:()->Void = {}
@IBOutlet weak var assetImage: UIImageView!
override func awakeFromNib() {
......@@ -24,16 +28,29 @@ class TrashContenAssetCell: UICollectionViewCell {
var model:AssetModel?{
didSet{
guard let model = model else{
return
guard let model = model else{return}
if self.mediaType == .video {
PhotoAndVideoMananger.mananger.getPreImageFromVideo(identifier: model.localIdentifier, completed: { [weak self] image in
guard let self else {return}
DispatchQueue.main.async {
self.assetImage.image = image
}
})
}else {
DispatchQueue.main.async {
self.assetImage.image = PhotoAndVideoMananger.mananger.getImageFromAssetID(id: model.localIdentifier)
}
}
}
// assetImage.im
}
@IBAction func removeClick(_ sender: Any) {
// 移除数据
if let model = self.model {
TrashDataManager.revokeTrashData(mediaType: self.mediaType,identifier: model.localIdentifier) {
self.revokeCallBack()
}
}
}
......
......@@ -11,6 +11,13 @@ class TrashContenView: UIView {
var collectionView: UICollectionView!
var mediaType : TrashTypeEnum?
var defaultView : TrashDefaultView = {
let view = TrashDefaultView()
return view
}()
var typeLabel:UILabel!
var sizeLabel:UILabel!
......@@ -19,10 +26,16 @@ class TrashContenView: UIView {
var lineThree: UIView!
var lineFour: UIView!
var deleteButton : UIButton?
var scrollLine:UIView!
let lineW:CGFloat = (ScreenW - 62) / 4.0
var dataSource:[AssetModel] = []
var dataSource:[AssetModel] = [] {
didSet{
self.resetContenViewUI()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
......@@ -45,6 +58,18 @@ class TrashContenView: UIView {
/// 当数据为空的时候显示默认页面
func setDefauleUI(){
self.collectionView.removeFromSuperview()
self.defaultView.mediaType = self.mediaType
self.addSubview(self.defaultView)
self.defaultView.snp.makeConstraints { make in
make.left.top.right.bottom.equalToSuperview()
}
}
func configUI(){
let layout = UICollectionViewFlowLayout()
layout.minimumInteritemSpacing = 12
......@@ -254,6 +279,75 @@ extension TrashContenView:UICollectionViewDelegate,UICollectionViewDataSource,UI
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TrashContenAssetCell", for: indexPath) as! TrashContenAssetCell
// 设置image
cell.mediaType = self.trashType
cell.model = self.dataSource[indexPath.item]
cell.revokeCallBack = {
// 重新设置数据源
self.dataSource = TrashDataManager.getCurrentMediaTypeTrashData(mediaType: self.mediaType)
}
return cell
}
/// 重新刷新页面UI
private func resetContenViewUI(){
DispatchQueue.main.async {
// 设置数量
if self.trashType == .video{
self.sizeLabel.text = "\(self.dataSource.count) \(self.getUnits()) · \(self.getDataSize(array: self.dataSource))"
}else{
self.sizeLabel.text = "\(self.dataSource.count) \(self.getUnits()) · \(self.getDataSize(array: self.dataSource))"
}
// 设置标题
self.typeLabel.text = "Trash Can · \(self.trashType.rawValue)"
// 设置button的文字和样式
self.setDeleteButtonUI()
if self.dataSource.count > 0 {
self.collectionView.reloadData()
}else {
self.setDefauleUI()
}
}
}
/// 获取资源总大小
/// - Parameter array: 资源数组
/// - Returns: 格式化后的总大小
func getDataSize(array : [AssetModel]) -> String{
var size : Double = 0.0
for item in array {
size = size + item.assetSize
}
return formatFileSize(size)
}
private func setDeleteButtonUI(){
if let deleteButton = self.deleteButton {
if self.dataSource.count <= 0{
deleteButton.setTitle("Delete", for: .normal)
deleteButton.backgroundColor = UIColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1)
return
}
deleteButton.backgroundColor = UIColor(red: 0, green: 0.51, blue: 1, alpha: 1)
if self.trashType == .video{
deleteButton.setTitle("Delete \(self.dataSource.count) \(self.getUnits())", for: .normal)
}else {
deleteButton.setTitle("Delete \(self.dataSource.count) \(self.getUnits())", for: .normal)
}
}
}
// 获取单位复数
private func getUnits()->String{
if self.dataSource.count > 1 {
return self.trashType == .video ? "videos" : "photos"
}else {
return self.trashType == .video ? "video" : "photo"
}
}
}
//
// TrashDefaultView.swift
// PhoneManager
//
// Created by edy on 2025/5/15.
//
import UIKit
class TrashDefaultView: UIView {
static let jumpToPhotosDetailPageName = Notification.Name("jumpToPhotosDetailPageName")
static let jumpToVideosDetailPageName = Notification.Name("jumpToVideosDetailPageName")
var mediaType : TrashTypeEnum?
lazy var logImageView : UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "Frame 4")
return imageView
}()
lazy var tipLabel: UILabel = {
let label = UILabel()
label.text = "Your trash can is empty!!"
label.textAlignment = .center
label.textColor = UIColor(red: 0.07, green: 0.07, blue: 0.07, alpha: 1)
label.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
label.backgroundColor = .clear
return label
}()
lazy var detailTiplabel: UILabel = {
let label = UILabel()
label.text = "Once you start sending the video photos to the trash can, they will be displayed here."
label.textColor = UIColor(red: 0.4, green: 0.4, blue: 0.4, alpha: 1)
label.textAlignment = .center
label.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
label.backgroundColor = .clear
label.numberOfLines = 0
return label
}()
lazy var startButton : UIButton = {
let view = UIButton()
view.setTitle("Start cleaning", for: UIControl.State.normal)
view.setTitleColor(UIColor(red: 1, green: 1, blue: 1, alpha: 1), for: .normal)
view.titleLabel?.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
view.clipsToBounds = true
view.layer.cornerRadius = 16.5
view.backgroundColor = UIColor(red: 0, green: 0.51, blue: 1, alpha: 1)
view.addTarget(self, action: #selector(startButtonAction), for: .touchUpInside)
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.addSubview(self.logImageView)
self.addSubview(self.tipLabel)
self.addSubview(self.detailTiplabel)
self.addSubview(self.startButton)
self.logImageView.snp.makeConstraints { make in
make.width.equalTo(230)
make.height.equalTo(154)
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(105)
}
self.tipLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(15)
make.right.equalToSuperview().offset(-15)
make.top.equalTo(self.logImageView.snp.bottom).offset(30)
make.height.equalTo(22)
}
self.detailTiplabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(15)
make.right.equalToSuperview().offset(-15)
make.top.equalTo(self.tipLabel.snp.bottom).offset(5)
make.height.equalTo(44)
}
self.startButton.snp.makeConstraints { make in
make.width.equalTo(107)
make.height.equalTo(33)
make.top.equalTo(self.detailTiplabel.snp.bottom).offset(30)
make.centerX.equalToSuperview()
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func startButtonAction(){
// 跳转页面
if let type = self.mediaType {
switch type {
case .video:
jumpToVideoDetailPage(type: type)
break
case .other,.shot:
jumpToPhotosDetailPage(type: type)
break
case .chat:
break
}
}
}
func jumpToPhotosDetailPage(type: TrashTypeEnum){
self.responderViewController()?.dismiss(animated: true, completion: {
NotificationCenter.default.post(name: TrashDefaultView.jumpToPhotosDetailPageName, object: nil,userInfo: ["type":self.mediaType?.rawValue ?? ""])
})
// PhotoDataManager.manager.loadFromFileSystem { model in
// let data = type == .other ? model.otherModelArray[4] : model.otherModelArray[2]
// let vc:HomePhotosDetailViewController = HomePhotosDetailViewController(model: data)
// vc.mediaType = type == .other ? .Other : PhotsFileType.screenshots
// vc.dealData()
// self.responderViewController()?.navigationController?.pushViewController(vc, animated: true)
// }
}
func jumpToVideoDetailPage(type: TrashTypeEnum){
self.responderViewController()?.dismiss(animated: true, completion: {
NotificationCenter.default.post(name: TrashDefaultView.jumpToVideosDetailPageName, object: nil)
})
}
}
......@@ -670,9 +670,9 @@ class PhotoAndVideoMananger {
if success {
suc()
} else if let error = error {
PMLoadingHUD.share.disMiss()
print("删除失败: \(error.localizedDescription)")
}
PMLoadingHUD.share.disMiss()
}
}
......@@ -1329,4 +1329,71 @@ extension PhotoAndVideoMananger{
return zip(hash1, hash2).filter { $0 != $1 }.count
}
/// 根据LocalIdentifier获取视频的URL
/// - Parameters:
/// - localIdentifier: 资源标识
/// - completion: 回调
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 = true // 允许从网络下载
options.deliveryMode = .automatic // 要求高质量格式
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"]))
}
}
}
}
/// 获取视频第一帧图片显示
/// - Parameters:
/// - identifier: 图片唯一标识
/// - completed: 回调
func getPreImageFromVideo(identifier : String?,completed: @escaping (UIImage)->Void){
if identifier == nil{
completed(UIImage())
return
}
let options = PHImageRequestOptions()
options.version = .current
options.deliveryMode = .highQualityFormat
options.isNetworkAccessAllowed = true
options.isSynchronous = false
if let videoAsset = PhotoAndVideoMananger.mananger.getPHAsssetwithID(ids: [identifier!]){
PHImageManager.default().requestImage(for: videoAsset, targetSize: CGSize(width: 400, height: 400), contentMode: PHImageContentMode.aspectFit, options: options) { image, _ in
if let thumbnailImage = image {
completed(thumbnailImage)
} else {
completed(UIImage())
}
}
}else {
completed(UIImage())
}
}
}
......@@ -63,7 +63,6 @@ class PMScaleImageView: UIView , UIScrollViewDelegate {
}()
func scrollViewDidZoom(_ scrollView: UIScrollView) {
Print(scrollView.zoomScale)
if scrollView.zoomScale > 1.5 {
self.Radius = 16
}else{
......
......@@ -12,6 +12,14 @@ var blurKey = "blurKey"
extension UIView {
func addTopShadow() {
layer.masksToBounds = false
layer.shadowColor = UIColor(red: 0, green: 0.51, blue: 1, alpha: 0.15).cgColor
layer.shadowOpacity = 1
layer.shadowOffset = CGSize(width: 0, height: -2) // 负值使阴影显示在顶部
layer.shadowRadius = 14
}
func responderViewController() -> UIViewController? {
for view in sequence(first: self.superview, next: { $0?.superview }) {
......
......@@ -19,6 +19,10 @@ class Singleton {
var maxDeleteCount : Int = 25
var trashData : [TrashTypeEnum : [AssetModel]] = [:]
var keepList : [TrashTypeEnum : [AssetModel]] = [:]
var photoPermission : PrivacyType?
......
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