Commit e667bf42 authored by CZ1004's avatar CZ1004

【新增】左滑垃圾桶功能

parent e3c81dec
//
// GroupDatabase.swift
// PhoneManager
//
// Created by edy on 2025/5/12.
//
import Foundation
import SQLite3
class GroupDatabase {
static let shared = GroupDatabase()
private var db: OpaquePointer?
private let dbPath: String
private init() {
let fileURL = try! FileManager.default
.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent("maintaiGroup.sqlite")
dbPath = fileURL.path
if sqlite3_open(dbPath, &db) != SQLITE_OK {
print("无法打开数据库")
return
}
createTable()
}
private func createTable() {
let createTableString = """
CREATE TABLE IF NOT EXISTS groups(
localIdentifier TEXT PRIMARY KEY,
assetSize DOUBLE,
createDate DOUBLE,
mediaType INTEGER,
groupId TEXT
);
"""
var createTableStatement: OpaquePointer?
if sqlite3_prepare_v2(db, createTableString, -1, &createTableStatement, nil) == SQLITE_OK {
if sqlite3_step(createTableStatement) == SQLITE_DONE {
print("成功创建groups表")
} else {
print("创建表失败")
}
} else {
print("创建表语句准备失败")
}
sqlite3_finalize(createTableStatement)
}
// 插入数据(带去重检查)
func insert(localIdentifier: String, assetSize: Double, createDate: Date, mediaType: Int, groupId: String) -> Bool {
// 先检查是否已存在相同的 localIdentifier
if let _ = query(localIdentifier: localIdentifier) {
// 如果已存在,则执行更新操作
return update(localIdentifier: localIdentifier, assetSize: assetSize, createDate: createDate, mediaType: mediaType, groupId: groupId)
}
// 如果不存在,执行插入操作
let insertStatementString = "INSERT INTO groups (localIdentifier, assetSize, createDate, mediaType, groupId) VALUES (?, ?, ?, ?, ?);"
var insertStatement: OpaquePointer?
if sqlite3_prepare_v2(db, insertStatementString, -1, &insertStatement, nil) == SQLITE_OK {
sqlite3_bind_text(insertStatement, 1, (localIdentifier as NSString).utf8String, -1, nil)
sqlite3_bind_double(insertStatement, 2, assetSize)
sqlite3_bind_double(insertStatement, 3, createDate.timeIntervalSince1970)
sqlite3_bind_int(insertStatement, 4, Int32(mediaType))
sqlite3_bind_text(insertStatement, 5, (groupId as NSString).utf8String, -1, nil)
if sqlite3_step(insertStatement) == SQLITE_DONE {
print("成功插入数据")
sqlite3_finalize(insertStatement)
return true
} else {
print("插入数据失败")
}
}
sqlite3_finalize(insertStatement)
return false
}
// 删除数据
func delete(localIdentifier: String) -> Bool {
let deleteStatementString = "DELETE FROM groups WHERE localIdentifier = ?;"
var deleteStatement: OpaquePointer?
if sqlite3_prepare_v2(db, deleteStatementString, -1, &deleteStatement, nil) == SQLITE_OK {
sqlite3_bind_text(deleteStatement, 1, (localIdentifier as NSString).utf8String, -1, nil)
if sqlite3_step(deleteStatement) == SQLITE_DONE {
print("成功删除数据")
sqlite3_finalize(deleteStatement)
return true
} else {
print("删除数据失败")
}
}
sqlite3_finalize(deleteStatement)
return false
}
// 更新数据
func update(localIdentifier: String, assetSize: Double, createDate: Date, mediaType: Int, groupId: String) -> Bool {
let updateStatementString = "UPDATE groups SET assetSize = ?, createDate = ?, mediaType = ?, groupId = ? WHERE localIdentifier = ?;"
var updateStatement: OpaquePointer?
if sqlite3_prepare_v2(db, updateStatementString, -1, &updateStatement, nil) == SQLITE_OK {
sqlite3_bind_double(updateStatement, 1, assetSize)
sqlite3_bind_double(updateStatement, 2, createDate.timeIntervalSince1970)
sqlite3_bind_int(updateStatement, 3, Int32(mediaType))
sqlite3_bind_text(updateStatement, 4, (groupId as NSString).utf8String, -1, nil)
sqlite3_bind_text(updateStatement, 5, (localIdentifier as NSString).utf8String, -1, nil)
if sqlite3_step(updateStatement) == SQLITE_DONE {
print("成功更新数据")
sqlite3_finalize(updateStatement)
return true
} else {
print("更新数据失败")
}
}
sqlite3_finalize(updateStatement)
return false
}
// 查询所有数据
func queryAll() -> [(localIdentifier: String, assetSize: Double, createDate: Date, mediaType: Int, groupId: String)] {
let queryStatementString = "SELECT * FROM groups;"
var queryStatement: OpaquePointer?
var result: [(localIdentifier: String, assetSize: Double, createDate: Date, mediaType: Int, groupId: String)] = []
if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
while sqlite3_step(queryStatement) == SQLITE_ROW {
let localIdentifier = String(cString: sqlite3_column_text(queryStatement, 0))
let assetSize = sqlite3_column_double(queryStatement, 1)
let createDate = Date(timeIntervalSince1970: sqlite3_column_double(queryStatement, 2))
let mediaType = Int(sqlite3_column_int(queryStatement, 3))
let groupId = String(cString: sqlite3_column_text(queryStatement, 4))
result.append((localIdentifier, assetSize, createDate, mediaType, groupId))
}
}
sqlite3_finalize(queryStatement)
return result
}
// 根据localIdentifier查询单条数据
func query(localIdentifier: String) -> (localIdentifier: String, assetSize: Double, createDate: Date, mediaType: Int, groupId: String)? {
let queryStatementString = "SELECT * FROM groups WHERE localIdentifier = ?;"
var queryStatement: OpaquePointer?
if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
sqlite3_bind_text(queryStatement, 1, (localIdentifier as NSString).utf8String, -1, nil)
if sqlite3_step(queryStatement) == SQLITE_ROW {
let localIdentifier = String(cString: sqlite3_column_text(queryStatement, 0))
let assetSize = sqlite3_column_double(queryStatement, 1)
let createDate = Date(timeIntervalSince1970: sqlite3_column_double(queryStatement, 2))
let mediaType = Int(sqlite3_column_int(queryStatement, 3))
let groupId = String(cString: sqlite3_column_text(queryStatement, 4))
sqlite3_finalize(queryStatement)
return (localIdentifier, assetSize, createDate, mediaType, groupId)
}
}
sqlite3_finalize(queryStatement)
return nil
}
// 根据mediaType查询数据
func queryByMediaType(_ mediaType: Int) -> [(localIdentifier: String, assetSize: Double, createDate: Date, mediaType: Int, groupId: String)] {
let queryStatementString = "SELECT * FROM groups WHERE mediaType = ?;"
var queryStatement: OpaquePointer?
var result: [(localIdentifier: String, assetSize: Double, createDate: Date, mediaType: Int, groupId: String)] = []
if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
sqlite3_bind_int(queryStatement, 1, Int32(mediaType))
while sqlite3_step(queryStatement) == SQLITE_ROW {
let localIdentifier = String(cString: sqlite3_column_text(queryStatement, 0))
let assetSize = sqlite3_column_double(queryStatement, 1)
let createDate = Date(timeIntervalSince1970: sqlite3_column_double(queryStatement, 2))
let mediaType = Int(sqlite3_column_int(queryStatement, 3))
let groupId = String(cString: sqlite3_column_text(queryStatement, 4))
result.append((localIdentifier, assetSize, createDate, mediaType, groupId))
}
}
sqlite3_finalize(queryStatement)
return result
}
// 根据groupId查询数据
func queryByGroupId(_ groupId: String) -> [(localIdentifier: String, assetSize: Double, createDate: Date, mediaType: Int, groupId: String)] {
let queryStatementString = "SELECT * FROM groups WHERE groupId = ?;"
var queryStatement: OpaquePointer?
var result: [(localIdentifier: String, assetSize: Double, createDate: Date, mediaType: Int, groupId: String)] = []
if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
sqlite3_bind_text(queryStatement, 1, (groupId as NSString).utf8String, -1, nil)
while sqlite3_step(queryStatement) == SQLITE_ROW {
let localIdentifier = String(cString: sqlite3_column_text(queryStatement, 0))
let assetSize = sqlite3_column_double(queryStatement, 1)
let createDate = Date(timeIntervalSince1970: sqlite3_column_double(queryStatement, 2))
let mediaType = Int(sqlite3_column_int(queryStatement, 3))
let groupId = String(cString: sqlite3_column_text(queryStatement, 4))
result.append((localIdentifier, assetSize, createDate, mediaType, groupId))
}
}
sqlite3_finalize(queryStatement)
return result
}
// 批量删除数据
func batchDelete(localIdentifiers: [String]) -> Bool {
// 使用事务来确保原子性
let beginTransaction = "BEGIN TRANSACTION;"
let commitTransaction = "COMMIT TRANSACTION;"
let rollbackTransaction = "ROLLBACK TRANSACTION;"
// 准备删除语句
let deleteStatementString = "DELETE FROM groups 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)
}
}
...@@ -197,7 +197,65 @@ class TrashDatabase { ...@@ -197,7 +197,65 @@ class TrashDatabase {
sqlite3_finalize(queryStatement) sqlite3_finalize(queryStatement)
return result 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 { deinit {
sqlite3_close(db) sqlite3_close(db)
} }
......
...@@ -26,7 +26,7 @@ class ChargeGuideController : BaseViewController,UIScrollViewDelegate,UINavigati ...@@ -26,7 +26,7 @@ class ChargeGuideController : BaseViewController,UIScrollViewDelegate,UINavigati
"Scroll the page and select Charger section", "Scroll the page and select Charger section",
"Select 'ls Connected' and 'Run lmmediately',then tap on Next", "Select 'ls Connected' and 'Run lmmediately',then tap on Next",
"Tap on New Blank Automation to connect the app", "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 let numberOfPages = 7
......
...@@ -227,10 +227,6 @@ class CompressSelectCell : UICollectionViewCell { ...@@ -227,10 +227,6 @@ class CompressSelectCell : UICollectionViewCell {
@objc func imageClick(){ @objc func imageClick(){
if self.currentMediaType == .compressPhoto { if self.currentMediaType == .compressPhoto {
// 点击之后跳转详情页面 // 点击之后跳转详情页面
if let tempModel = self.model { if let tempModel = self.model {
......
...@@ -13,6 +13,8 @@ class VideoViewController: UIViewController { ...@@ -13,6 +13,8 @@ class VideoViewController: UIViewController {
var url : URL? var url : URL?
var isAutoPlay : Bool = true
// 系统播放器控制器 // 系统播放器控制器
let playerViewController = AVPlayerViewController() let playerViewController = AVPlayerViewController()
...@@ -38,7 +40,9 @@ class VideoViewController: UIViewController { ...@@ -38,7 +40,9 @@ class VideoViewController: UIViewController {
playerViewController.didMove(toParent: self) playerViewController.didMove(toParent: self)
// 4. 自动播放 // 4. 自动播放
player.play() if self.isAutoPlay {
player.play()
}
} }
} }
......
...@@ -253,40 +253,8 @@ extension HomeVideoDetailController:WaterfallMutiSectionDelegate,UICollectionVie ...@@ -253,40 +253,8 @@ extension HomeVideoDetailController:WaterfallMutiSectionDelegate,UICollectionVie
// 获取视频的图片 // 获取视频的图片
PhotoAndVideoMananger.mananger.getVideoImageByIdent(ident: ident) { image in PhotoAndVideoMananger.mananger.getVideoImageByIdent(ident: ident) { image in
// 点击之后跳转详情页面 // 点击之后跳转详情页面
let vc = PMShowImgVideoController() let vc = PhotoRemoveViewController(data: self.resourceData,currentIndex: indexPath.row,mediaType: .video)
vc.state = .similarVideos self.navigationController?.pushViewController(vc, animated: true)
vc.currentIdx = 0
let dataSource = ImageSeletedCollectionItem()
dataSource.isSeleted = cell.choose
dataSource.id = ident
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{
cell.choose = item.isSeleted ?? false
}
}
}
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)
}
}
}
} errorHandler: { } errorHandler: {
DispatchQueue.main.async { DispatchQueue.main.async {
let alert = UIAlertController(title: nil, message: "Get Video image failure", preferredStyle: .alert) let alert = UIAlertController(title: nil, message: "Get Video image failure", preferredStyle: .alert)
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
import UIKit import UIKit
import SnapKit import SnapKit
import Photos
class PhotoRemoveViewController: BaseViewController { class PhotoRemoveViewController: BaseViewController {
...@@ -63,7 +64,9 @@ class PhotoRemoveViewController: BaseViewController { ...@@ -63,7 +64,9 @@ class PhotoRemoveViewController: BaseViewController {
super.viewDidLoad() super.viewDidLoad()
formatter.dateStyle = .medium formatter.dateStyle = .medium
formatter.timeStyle = .none formatter.timeStyle = .none
if let type = self.mediaType {
self.navView.mediaType = type
}
self.view.addSubview(self.navView) self.view.addSubview(self.navView)
self.navView.snp.makeConstraints { make in self.navView.snp.makeConstraints { make in
make.top.left.right.equalToSuperview() make.top.left.right.equalToSuperview()
...@@ -78,14 +81,36 @@ class PhotoRemoveViewController: BaseViewController { ...@@ -78,14 +81,36 @@ class PhotoRemoveViewController: BaseViewController {
make.height.equalTo(78) make.height.equalTo(78)
} }
getData() self.trashSubView.clearTashDataCallBack = {[weak self] in
guard let self else {return}
// 清除垃圾桶数据
TrashDataManager.clearTrashData(mediaType: self.mediaType) {[weak self] in
guard let self else {return}
self.showCurrentPageUIWhenTashDataChanged()
}
}
self.trashSubView.presentTashDetailViewClickCallBack = {
// 进入垃圾桶详情页面
DispatchQueue.main.async {
let vc : TrashViewController = TrashViewController()
self.present(vc, animated: true)
}
}
showCurrentPageUIWhenTashDataChanged()
} }
func changeDateToString() -> String{ func changeDateToString() -> String{
let model = self.dataModel[self.currentIndex] let model = self.dataModel[self.currentIndex]
return formatter.string(from: model.createDate) return formatter.string(from: model.createDate)
} }
func chengeSizeToString()-> String {
let model = self.dataModel[self.currentIndex]
return formatFileSize(model.assetSize)
}
func showTrashView(){ func showTrashView(){
DispatchQueue.main.async { DispatchQueue.main.async {
self.trashSubView.isHidden = false self.trashSubView.isHidden = false
...@@ -106,7 +131,7 @@ class PhotoRemoveViewController: BaseViewController { ...@@ -106,7 +131,7 @@ class PhotoRemoveViewController: BaseViewController {
} }
} }
func getData(){ func showCurrentPageUIWhenTashDataChanged(){
if let type = self.mediaType{ if let type = self.mediaType{
// 从数据库拿数据 // 从数据库拿数据
let dataDB = TrashDatabase.shared.queryByMediaType(type.dbType) let dataDB = TrashDatabase.shared.queryByMediaType(type.dbType)
...@@ -115,24 +140,27 @@ class PhotoRemoveViewController: BaseViewController { ...@@ -115,24 +140,27 @@ class PhotoRemoveViewController: BaseViewController {
assetModel.append(AssetModel.init(localIdentifier: item.localIdentifier, assetSize: item.assetSize, createDate: item.createDate, mediaType: item.mediaType)) assetModel.append(AssetModel.init(localIdentifier: item.localIdentifier, assetSize: item.assetSize, createDate: item.createDate, mediaType: item.mediaType))
} }
// 从单利拿数据 // 从单利拿数据
if let type = self.mediaType{ DispatchQueue.main.async {
if let dataSg = Singleton.shared.trashData[type]{ if let type = self.mediaType{
assetModel = assetModel + dataSg if let dataSg = Singleton.shared.trashData[type]{
if assetModel.count > 0 { assetModel = assetModel + dataSg
// 显示垃圾桶 // 如果单利中有当前数据,显示撤回按钮
self.trashSubView.resourceCountlabel.text = String(assetModel.count)
self.showTrashView() if dataSg.count > 0{
}else{ self.navView.resetButton.isHidden = false
self.hideTrashView() }else{
} self.navView.resetButton.isHidden = true
// 如果单利中有当前数据,显示撤回按钮 }
if dataSg.count > 0{
self.navView.resetButton.isHidden = false }else {
}else{
self.navView.resetButton.isHidden = true self.navView.resetButton.isHidden = true
} }
}else { }
self.navView.resetButton.isHidden = true if assetModel.count > 0 {
// 显示垃圾桶
self.trashSubView.resourceCountlabel.text = String(assetModel.count)
self.showTrashView()
}else{
self.hideTrashView() self.hideTrashView()
} }
} }
...@@ -144,6 +172,7 @@ class PhotoRemoveViewController: BaseViewController { ...@@ -144,6 +172,7 @@ class PhotoRemoveViewController: BaseViewController {
// 只创建两个视图 // 只创建两个视图
for _ in 0..<2 { for _ in 0..<2 {
let photoView = PhotosRemoveBaseView() let photoView = PhotosRemoveBaseView()
photoView.mediaType = self.mediaType
photoView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(handlePan))) photoView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(handlePan)))
view.addSubview(photoView) view.addSubview(photoView)
photoView.frame = CGRectMake(15, statusBarHeight + 44, self.view.width - 30, self.view.height - 78 - safeHeight - statusBarHeight - 44) photoView.frame = CGRectMake(15, statusBarHeight + 44, self.view.width - 30, self.view.height - 78 - safeHeight - statusBarHeight - 44)
...@@ -167,7 +196,7 @@ class PhotoRemoveViewController: BaseViewController { ...@@ -167,7 +196,7 @@ class PhotoRemoveViewController: BaseViewController {
Singleton.shared.trashData[type] = [self.dataModel[currentIndex]] Singleton.shared.trashData[type] = [self.dataModel[currentIndex]]
} }
} }
self.getData() self.showCurrentPageUIWhenTashDataChanged()
} }
func saveDataToTrashDB(){ func saveDataToTrashDB(){
if let type = self.mediaType{ if let type = self.mediaType{
...@@ -181,7 +210,7 @@ class PhotoRemoveViewController: BaseViewController { ...@@ -181,7 +210,7 @@ class PhotoRemoveViewController: BaseViewController {
Singleton.shared.trashData[type] = [] Singleton.shared.trashData[type] = []
} }
} }
getData() showCurrentPageUIWhenTashDataChanged()
} }
// 将数据存到保留列表 // 将数据存到保留列表
...@@ -189,9 +218,16 @@ class PhotoRemoveViewController: BaseViewController { ...@@ -189,9 +218,16 @@ class PhotoRemoveViewController: BaseViewController {
// 如果单利中有数据,那么先把单利中的数据存到数据库 // 如果单利中有数据,那么先把单利中的数据存到数据库
saveDataToTrashDB() saveDataToTrashDB()
//FIXME: 保留当前图片到保留列表 // 保留当前图片到保留列表
if let type = self.mediaType{
let item = self.dataModel[currentIndex]
let success = GroupDatabase.shared.insert(localIdentifier: item.localIdentifier, assetSize: item.assetSize, createDate: item.createDate, mediaType: type.dbType, groupId: UUID().uuidString)
if success {
Print("存入保留列表成功")
}else{
Print("存入保留列表失败")
}
}
} }
...@@ -253,10 +289,21 @@ class PhotoRemoveViewController: BaseViewController { ...@@ -253,10 +289,21 @@ class PhotoRemoveViewController: BaseViewController {
private func updateViewContents() { private func updateViewContents() {
for (index, view) in photoViews.enumerated() { for (index, view) in photoViews.enumerated() {
let imageIndex = (currentIndex + index) % dataModel.count let imageIndex = (currentIndex + index) % dataModel.count
view.configure(with: PhotoAndVideoMananger.mananger.getImageFromAssetID(id: dataModel[imageIndex].localIdentifier) ?? UIImage()) if self.mediaType == .video{
self.getVideoURLFromLocalIdentifier(localIdentifier: dataModel[imageIndex].localIdentifier) { url, error in
if url != nil {
view.url = url
view.reload(index: index)
}
}
}else {
view.configure(with: PhotoAndVideoMananger.mananger.getImageFromAssetID(id: dataModel[imageIndex].localIdentifier) ?? UIImage())
}
} }
// 设置顶部title // 设置顶部title
self.navView.titleLbel.text = changeDateToString() self.navView.titleLbel.text = changeDateToString()
self.navView.sizeLbel.text = chengeSizeToString()
} }
private func updateActionButton(for view: PhotosRemoveBaseView, translation: CGPoint) { private func updateActionButton(for view: PhotosRemoveBaseView, translation: CGPoint) {
...@@ -289,7 +336,7 @@ class PhotoRemoveViewController: BaseViewController { ...@@ -289,7 +336,7 @@ class PhotoRemoveViewController: BaseViewController {
private func resetViewPosition(_ view: PhotosRemoveBaseView) { private func resetViewPosition(_ view: PhotosRemoveBaseView) {
UIView.animate(withDuration: 0.3) { UIView.animate(withDuration: 0.3) {
view.transform = .identity view.transform = .identity
view.center = self.view.center view.frame = CGRectMake(15, statusBarHeight + 44, self.view.width - 30, self.view.height - 78 - safeHeight - statusBarHeight - 44)
view.hideButtons() view.hideButtons()
} }
} }
...@@ -324,4 +371,40 @@ class PhotoRemoveViewController: BaseViewController { ...@@ -324,4 +371,40 @@ class PhotoRemoveViewController: BaseViewController {
view.bringSubviewToFront(photoViews[1]) view.bringSubviewToFront(photoViews[1])
view.bringSubviewToFront(photoViews[0]) view.bringSubviewToFront(photoViews[0])
} }
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 = 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"]))
}
}
}
}
} }
...@@ -6,15 +6,32 @@ ...@@ -6,15 +6,32 @@
// //
import UIKit import UIKit
import SnapKit
class PhotoRemoveNavView: UIView { class PhotoRemoveNavView: UIView {
public var backButton:UIButton! public var backButton:UIButton!
public var sizeLbel : UILabel!
public var titleLbel : UILabel! public var titleLbel : UILabel!
public var resetButton :UIButton! public var resetButton :UIButton!
private var topConstraint: Constraint?
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) { override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
...@@ -41,6 +58,18 @@ class PhotoRemoveNavView: UIView { ...@@ -41,6 +58,18 @@ class PhotoRemoveNavView: UIView {
make.width.height.equalTo(iconWH) 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 = UILabel()
titleLbel.textColor = UIColor(red: 0.07, green: 0.07, blue: 0.07, alpha: 1) titleLbel.textColor = UIColor(red: 0.07, green: 0.07, blue: 0.07, alpha: 1)
titleLbel.font = UIFont.systemFont(ofSize: 12, weight: .medium) titleLbel.font = UIFont.systemFont(ofSize: 12, weight: .medium)
...@@ -48,7 +77,7 @@ class PhotoRemoveNavView: UIView { ...@@ -48,7 +77,7 @@ class PhotoRemoveNavView: UIView {
titleLbel.snp.makeConstraints { make in titleLbel.snp.makeConstraints { make in
make.centerX.equalToSuperview() make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(6 + statusBarHeight) self.topConstraint = make.top.equalToSuperview().offset(6 + statusBarHeight).constraint
make.height.equalTo(22) make.height.equalTo(22)
} }
......
...@@ -14,45 +14,76 @@ class PhotosRemoveBaseView: UIView { ...@@ -14,45 +14,76 @@ class PhotosRemoveBaseView: UIView {
var initialCenter: CGPoint = .zero 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 imageView = UIImageView()
private let leftButton = UIButton() private let leftButton = UIButton()
private let rightButton = UIButton() private let rightButton = UIButton()
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
self.layer.cornerRadius = 20
self.clipsToBounds = true
setupUI() setupUI()
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
private func setupUI() { private func setupUI() {
// 配置图片视图 // 配置图片视图
imageView.contentMode = .scaleAspectFill imageView.contentMode = .scaleAspectFit
imageView.layer.cornerRadius = 20
imageView.clipsToBounds = true
addSubview(imageView) addSubview(imageView)
// 配置按钮
let buttonSize: CGFloat = 300
leftButton.frame = CGRect(x: 0, y: 0, width: buttonSize, height: buttonSize) leftButton.frame = CGRect(x: self.width / 2 - 45.5, y: self.height / 2 - 21, width: 95, height: 42)
leftButton.setImage(UIImage(named:"Frame 2"), for: .normal) // 替换你的图标 leftButton.setTitle("Delete", for: .normal)
leftButton.backgroundColor = UIColor(red: 0.95, green: 0.21, blue: 0.21, alpha: 1)
leftButton.tintColor = .white leftButton.tintColor = .white
leftButton.layer.cornerRadius = 21
leftButton.clipsToBounds = true
leftButton.alpha = 0 leftButton.alpha = 0
addSubview(leftButton) addSubview(leftButton)
rightButton.frame = CGRect(x: 0, y: 0, width: buttonSize, height: buttonSize) rightButton.frame = CGRect(x: self.width / 2 - 45.5, y: self.height / 2 - 21, width: 95, height: 42)
rightButton.setImage(UIImage(named:"Group_1171275102"), for: .normal) // 替换你的图标 rightButton.setTitle("Retain", for: .normal)
rightButton.backgroundColor = UIColor(red: 0.18, green: 0.76, blue: 0.35, alpha: 1)
rightButton.tintColor = .white rightButton.tintColor = .white
rightButton.layer.cornerRadius = 21
rightButton.clipsToBounds = true
rightButton.alpha = 0 rightButton.alpha = 0
addSubview(rightButton) addSubview(rightButton)
leftButton.center = cWindow!.center
rightButton.center = cWindow!.center
} }
func configure(with image: UIImage) { func configure(with image: UIImage) {
...@@ -61,15 +92,21 @@ class PhotosRemoveBaseView: UIView { ...@@ -61,15 +92,21 @@ class PhotosRemoveBaseView: UIView {
} }
func showLeftButton() { func showLeftButton() {
self.leftButton.center = self.center
UIView.animate(withDuration: 0.2) { UIView.animate(withDuration: 0.2) {
self.leftButton.alpha = 1 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 self.rightButton.alpha = 0
} }
} }
func showRightButton() { func showRightButton() {
self.rightButton.center = self.center
UIView.animate(withDuration: 0.2) { UIView.animate(withDuration: 0.2) {
self.rightButton.alpha = 1 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 self.leftButton.alpha = 0
} }
} }
...@@ -78,8 +115,9 @@ class PhotosRemoveBaseView: UIView { ...@@ -78,8 +115,9 @@ class PhotosRemoveBaseView: UIView {
UIView.animate(withDuration: 0.2) { UIView.animate(withDuration: 0.2) {
self.leftButton.alpha = 0 self.leftButton.alpha = 0
self.rightButton.alpha = 0 self.rightButton.alpha = 0
self.leftButton.center = cWindow!.center self.maskTempleteView.removeFromSuperview()
self.rightButton.center = cWindow!.center self.leftButton.center = self.center
self.rightButton.center = self.center
} }
} }
} }
...@@ -9,6 +9,10 @@ import UIKit ...@@ -9,6 +9,10 @@ import UIKit
class TrashSubView: UIView { class TrashSubView: UIView {
var clearTashDataCallBack : ()->Void = {}
var presentTashDetailViewClickCallBack : ()->Void = {}
lazy var resourceCountlabel : UILabel = { lazy var resourceCountlabel : UILabel = {
let label = UILabel() let label = UILabel()
label.font = UIFont.systemFont(ofSize: 24, weight: .semibold) label.font = UIFont.systemFont(ofSize: 24, weight: .semibold)
...@@ -33,14 +37,27 @@ class TrashSubView: UIView { ...@@ -33,14 +37,27 @@ class TrashSubView: UIView {
button.titleLabel?.font = UIFont.systemFont(ofSize: 12, weight: .semibold) button.titleLabel?.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
button.layer.cornerRadius = 19 button.layer.cornerRadius = 19
button.clipsToBounds = true button.clipsToBounds = true
button.addTarget(self, action: #selector(emptyButtonAction), for: .touchUpInside)
return button return button
}() }()
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
// 给当前视图添加手势
addTapGestureToSelf()
// 添加UI
addUIViews()
setUILocation()
}
private func addUIViews(){
self.addSubview(self.resourceCountlabel) self.addSubview(self.resourceCountlabel)
self.addSubview(self.tipLabel) self.addSubview(self.tipLabel)
self.addSubview(self.emptyButton) self.addSubview(self.emptyButton)
}
private func setUILocation(){
self.resourceCountlabel.snp.makeConstraints { make in self.resourceCountlabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16) make.left.equalToSuperview().offset(16)
make.centerY.equalToSuperview() make.centerY.equalToSuperview()
...@@ -59,7 +76,13 @@ class TrashSubView: UIView { ...@@ -59,7 +76,13 @@ class TrashSubView: UIView {
make.centerY.equalToSuperview() make.centerY.equalToSuperview()
make.width.equalTo(145) 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) { required init?(coder: NSCoder) {
...@@ -67,3 +90,12 @@ class TrashSubView: UIView { ...@@ -67,3 +90,12 @@ class TrashSubView: UIView {
} }
} }
extension TrashSubView {
@objc func emptyButtonAction(){
self.clearTashDataCallBack()
}
@objc func tapHandle(){
self.presentTashDetailViewClickCallBack()
}
}
...@@ -22,6 +22,26 @@ class TrashViewController: UIViewController { ...@@ -22,6 +22,26 @@ class TrashViewController: UIViewController {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
configUI() configUI()
getTrashDataAndRefreshPage()
}
/// 获取垃圾桶数据
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.dataSource = data
DispatchQueue.main.async {
tempView.collectionView.reloadData()
}
}
}
}
}
} }
......
//
// TrashDataManager.swift
// PhoneManager
//
// Created by edy on 2025/5/14.
//
import UIKit
class TrashDataManager {
/// 清除当前垃圾桶的数据
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()
clearCurrentMediaTypeTrashDBData()
// 删除完成之后重新
comlete()
}
}else{
comlete()
}
func clearCurrentMediaTypeTrashSigtonData(){
if let type = mediaType {
Singleton.shared.trashData[type] = []
Print("删除单利中当前垃圾桶数据成功")
}
}
func clearCurrentMediaTypeTrashDBData(){
if let type = mediaType {
let dataDB = TrashDatabase.shared.queryByMediaType(type.dbType)
let success = TrashDatabase.shared.batchDelete(localIdentifiers: dataDB.map({$0.localIdentifier}))
if success {
Print("删除数据库当前垃圾桶数据成功")
}else {
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,7 +7,7 @@ ...@@ -7,7 +7,7 @@
import Foundation import Foundation
enum TrashTypeEnum{ enum TrashTypeEnum : CaseIterable{
case video,other,shot,chat case video,other,shot,chat
var dbType:Int{ var dbType:Int{
......
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