Commit e3c81dec authored by CZ1004's avatar CZ1004

【新增】垃圾桶功能

parent 07f47302
......@@ -51,6 +51,26 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
return true
}
func applicationWillTerminate(_ application: UIApplication) {
// 保存单利中所有垃圾桶的数据
saveDataToTrashDB()
}
func saveDataToTrashDB(){
let data = Singleton.shared.trashData
var count = 0
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 {
count = count + 1
}
}
}
Singleton.shared.trashData = [:]
Print("保留单利数据到数据库--\(count)条")
}
func applicationDidBecomeActive(_ application: UIApplication) {
NotificationCenter.default.post(name: Notification.Name("applicationDidBecomeActive"), object: nil)
......
{
"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
}
}
......@@ -308,29 +308,15 @@ extension HomePhotosDetailViewController:WaterfallMutiSectionDelegate,UICollecti
cell.cellCallBack = {[weak self] ident,order in
guard let self else {return}
DispatchQueue.main.async {
// 点击之后跳转详情页面
let vc = PMShowImgVideoController()
vc.state = .similarPhotos
vc.currentIdx = 0
vc.oldPageIndexPath = indexPath
let dataSource = ImageSeletedCollectionItem()
dataSource.isSeleted = cell.choose
dataSource.id = ident
// 获取image
dataSource.image = PhotoAndVideoMananger.mananger.getImageFromAssetID(id: ident.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{
cell.choose = item.isSeleted ?? false
}
}
if self.mediaType == .screenshots {
let vc = PhotoRemoveViewController(data: self.resourceData,currentIndex: indexPath.row,mediaType: .shot)
self.navigationController?.pushViewController(vc, animated: true)
}
if self.mediaType == .Other {
let vc = PhotoRemoveViewController(data: self.resourceData,currentIndex: indexPath.row,mediaType: .other)
self.navigationController?.pushViewController(vc, animated: true)
}
self.navigationController?.pushViewController(vc, animated: true)
}
}
cell.clickCallBack = {[weak self] click,order in
......
//
// PhotoRemoveViewController.swift
// PhoneManager
//
// Created by edy on 2025/5/13.
//
import UIKit
import SnapKit
class PhotoRemoveViewController: BaseViewController {
var mediaType : TrashTypeEnum?
private var bottomConstraint: Constraint?
let formatter = DateFormatter()
lazy var navView : PhotoRemoveNavView = {
let view = PhotoRemoveNavView()
return view
}()
// MARK: - 属性
private var photoViews: [PhotosRemoveBaseView] = []
private var currentIndex : Int
private var dataModel: [AssetModel]
// 配置参数
private let rotationStrength: CGFloat = 320
private let rotationMax: CGFloat = .pi/4
private let scaleStrength: CGFloat = -0.5
private let scaleMax: CGFloat = 0.7
private let actionMargin: CGFloat = 120
lazy var trashSubView : TrashSubView = {
let view = TrashSubView()
view.backgroundColor = .white
view.isHidden = true
return view
}()
// MARK: - 初始化
init(data: [AssetModel],currentIndex:Int,mediaType:TrashTypeEnum) {
self.mediaType = mediaType
self.currentIndex = currentIndex
self.dataModel = data
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - 生命周期
override func viewDidLoad() {
super.viewDidLoad()
formatter.dateStyle = .medium
formatter.timeStyle = .none
self.view.addSubview(self.navView)
self.navView.snp.makeConstraints { make in
make.top.left.right.equalToSuperview()
make.height.equalTo(statusBarHeight + 44)
}
setupViews()
self.view.addSubview(self.trashSubView)
self.trashSubView.snp.makeConstraints { make in
make.left.right.equalToSuperview()
self.bottomConstraint = make.bottom.equalToSuperview().offset(78).constraint
make.height.equalTo(78)
}
getData()
}
func changeDateToString() -> String{
let model = self.dataModel[self.currentIndex]
return formatter.string(from: model.createDate)
}
func showTrashView(){
DispatchQueue.main.async {
self.trashSubView.isHidden = false
UIView.animate(withDuration: 0.1) {
// 更新约束
self.bottomConstraint?.update(offset: -safeHeight)
}
}
}
func hideTrashView(){
DispatchQueue.main.async {
UIView.animate(withDuration: 0.1) {
// 更新约束
self.bottomConstraint?.update(offset: -78)
}completion: { finished in
self.trashSubView.isHidden = true
}
}
}
func getData(){
if let type = self.mediaType{
// 从数据库拿数据
let dataDB = TrashDatabase.shared.queryByMediaType(type.dbType)
var assetModel : [AssetModel] = []
for item in dataDB {
assetModel.append(AssetModel.init(localIdentifier: item.localIdentifier, assetSize: item.assetSize, createDate: item.createDate, mediaType: item.mediaType))
}
// 从单利拿数据
if let type = self.mediaType{
if let dataSg = Singleton.shared.trashData[type]{
assetModel = assetModel + dataSg
if assetModel.count > 0 {
// 显示垃圾桶
self.trashSubView.resourceCountlabel.text = String(assetModel.count)
self.showTrashView()
}else{
self.hideTrashView()
}
// 如果单利中有当前数据,显示撤回按钮
if dataSg.count > 0{
self.navView.resetButton.isHidden = false
}else{
self.navView.resetButton.isHidden = true
}
}else {
self.navView.resetButton.isHidden = true
self.hideTrashView()
}
}
}
}
// MARK: - 视图设置
private func setupViews() {
// 只创建两个视图
for _ in 0..<2 {
let photoView = PhotosRemoveBaseView()
photoView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(handlePan)))
view.addSubview(photoView)
photoView.frame = CGRectMake(15, statusBarHeight + 44, self.view.width - 30, self.view.height - 78 - safeHeight - statusBarHeight - 44)
photoViews.append(photoView)
}
// 加载初始图片
updateViewContents()
bringCurrentViewToFront()
}
// 当保留的时候存数据
func saveDataToSigtonTrash(){
// 拿到单利数据
if let type = self.mediaType{
if let dataSg = Singleton.shared.trashData[type]{
var tempArray = dataSg
tempArray.append(self.dataModel[currentIndex])
Singleton.shared.trashData[type] = tempArray
}else{
Singleton.shared.trashData[type] = [self.dataModel[currentIndex]]
}
}
self.getData()
}
func saveDataToTrashDB(){
if let type = self.mediaType{
if let dataSg = Singleton.shared.trashData[type]{
for item in dataSg{
let success = TrashDatabase.shared.insert(localIdentifier: item.localIdentifier, assetSize: item.assetSize, createDate: item.createDate, mediaType: type.dbType)
if success {
Print("保留单利数据到数据库垃圾桶成功")
}
}
Singleton.shared.trashData[type] = []
}
}
getData()
}
// 将数据存到保留列表
func saveDataToKeepLib(){
// 如果单利中有数据,那么先把单利中的数据存到数据库
saveDataToTrashDB()
//FIXME: 保留当前图片到保留列表
}
// MARK: - 手势处理
@objc private func handlePan(_ gesture: UIPanGestureRecognizer) {
guard let activeView = gesture.view as? PhotosRemoveBaseView else { return }
// 记录初始位置
let initialCenter = activeView.center
let translation = gesture.translation(in: view)
let velocity = gesture.velocity(in: view)
switch gesture.state {
case .began:
activeView.initialCenter = initialCenter
case .changed:
// 使用增量位移更新位置
activeView.center = CGPoint(
x: activeView.initialCenter.x + translation.x,
y: activeView.initialCenter.y + translation.y
)
updateActionButton(for: activeView, translation: translation)
case .ended, .cancelled:
// 计算实际位移(基于最终位置)
let actualTranslation = CGPoint(
x: activeView.center.x - activeView.initialCenter.x,
y: activeView.center.y - activeView.initialCenter.y
)
// 使用实际位移判断
if abs(actualTranslation.x) > actionMargin || abs(velocity.x) > 500 {
removeCurrentView(translation: actualTranslation, velocity: velocity)
if actualTranslation.x > 0 {
// 保留操作
saveDataToKeepLib()
}
if actualTranslation.x < 0 {
// 删除操作,先存到单利
saveDataToSigtonTrash()
}
} else {
resetViewPosition(activeView)
}
gesture.setTranslation(.zero, in: view)
default: break
}
}
// MARK: - 视图更新逻辑
private func updateViewContents() {
for (index, view) in photoViews.enumerated() {
let imageIndex = (currentIndex + index) % dataModel.count
view.configure(with: PhotoAndVideoMananger.mananger.getImageFromAssetID(id: dataModel[imageIndex].localIdentifier) ?? UIImage())
}
// 设置顶部title
self.navView.titleLbel.text = changeDateToString()
}
private func updateActionButton(for view: PhotosRemoveBaseView, translation: CGPoint) {
if translation.x > 0 {
view.showRightButton()
} else if translation.x < 0 {
view.showLeftButton()
} else {
view.hideButtons()
}
}
private func removeCurrentView(translation: CGPoint, velocity: CGPoint) {
guard let activeView = photoViews.first else { return }
let direction: CGFloat = translation.x > 0 ? 1 : -1
let screenWidth = UIScreen.main.bounds.width
UIView.animate(withDuration: 0.3) {
activeView.center = CGPoint(
x: direction * (screenWidth * 1.5),
y: activeView.center.y + velocity.y * 0.1
)
activeView.alpha = 0
} completion: { _ in
self.recycleView()
}
}
private func resetViewPosition(_ view: PhotosRemoveBaseView) {
UIView.animate(withDuration: 0.3) {
view.transform = .identity
view.center = self.view.center
view.hideButtons()
}
}
private func recycleView() {
// 移除旧视图
let removedView = photoViews.removeFirst()
removedView.removeFromSuperview()
// 创建新视图并更新内容
let newView = PhotosRemoveBaseView()
newView.frame = CGRectMake(15, statusBarHeight + 44, self.view.width - 30, self.view.height - 78 - safeHeight - statusBarHeight - 44)
newView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(handlePan)))
newView.alpha = 0
view.addSubview(newView)
photoViews.append(newView)
// 更新索引并加载新内容
currentIndex = (currentIndex + 1) % dataModel.count
updateViewContents()
// 视图层级处理
bringCurrentViewToFront()
UIView.animate(withDuration: 0.3) {
newView.alpha = 1
}
}
private func bringCurrentViewToFront() {
guard photoViews.count >= 2 else { return }
view.bringSubviewToFront(photoViews[1])
view.bringSubviewToFront(photoViews[0])
}
}
......@@ -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
}
}
//
// PhotoRemoveNavView.swift
// PhoneManager
//
// Created by edy on 2025/5/13.
//
import UIKit
class PhotoRemoveNavView: UIView {
public var backButton:UIButton!
public var titleLbel : UILabel!
public var resetButton :UIButton!
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)
}
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()
make.top.equalToSuperview().offset(6 + statusBarHeight)
make.height.equalTo(22)
}
resetButton = UIButton()
resetButton.setImage(UIImage(named: "nav_back"), 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() {
}
}
//
// AnchorRotatableView.swift
// AIClean
//
// Created by 赵前 on 2025/5/10.
//
import Foundation
import UIKit
class PhotosRemoveBaseView: UIView {
var currentIndex : Int?
var initialCenter: CGPoint = .zero
private let imageView = UIImageView()
private let leftButton = UIButton()
private let rightButton = UIButton()
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupUI() {
// 配置图片视图
imageView.contentMode = .scaleAspectFill
imageView.layer.cornerRadius = 20
imageView.clipsToBounds = true
addSubview(imageView)
// 配置按钮
let buttonSize: CGFloat = 300
leftButton.frame = CGRect(x: 0, y: 0, width: buttonSize, height: buttonSize)
leftButton.setImage(UIImage(named:"Frame 2"), for: .normal) // 替换你的图标
leftButton.tintColor = .white
leftButton.alpha = 0
addSubview(leftButton)
rightButton.frame = CGRect(x: 0, y: 0, width: buttonSize, height: buttonSize)
rightButton.setImage(UIImage(named:"Group_1171275102"), for: .normal) // 替换你的图标
rightButton.tintColor = .white
rightButton.alpha = 0
addSubview(rightButton)
leftButton.center = cWindow!.center
rightButton.center = cWindow!.center
}
func configure(with image: UIImage) {
imageView.image = image
imageView.frame = bounds
}
func showLeftButton() {
UIView.animate(withDuration: 0.2) {
self.leftButton.alpha = 1
self.rightButton.alpha = 0
}
}
func showRightButton() {
UIView.animate(withDuration: 0.2) {
self.rightButton.alpha = 1
self.leftButton.alpha = 0
}
}
func hideButtons() {
UIView.animate(withDuration: 0.2) {
self.leftButton.alpha = 0
self.rightButton.alpha = 0
self.leftButton.center = cWindow!.center
self.rightButton.center = cWindow!.center
}
}
}
//
// TrashSubView.swift
// PhoneManager
//
// Created by edy on 2025/5/13.
//
import UIKit
class TrashSubView: UIView {
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
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.addSubview(self.resourceCountlabel)
self.addSubview(self.tipLabel)
self.addSubview(self.emptyButton)
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)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
......@@ -19,6 +19,8 @@ class Singleton {
var maxDeleteCount : Int = 25
var trashData : [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