Commit f1791fbd authored by CZ1004's avatar CZ1004

【新增】垃圾桶详情以及部分优化

parent b50a247c
......@@ -76,7 +76,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
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.dbType,groupId: uniqueId)
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("保存保留列表数据失败")
}
......
{
"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
}
}
......@@ -148,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}
......@@ -163,6 +167,7 @@ class CompressSelectCell : UICollectionViewCell {
}
}
}
}
override init(frame: CGRect) {
......
......@@ -256,6 +256,7 @@ extension CompressController:WaterfallMutiSectionDelegate,UICollectionViewDataSo
if self.currentResourceType != flag {
self.currentResourceType = flag
// 先移除下,防止点到
self.selectedModel.removeAll()
self.resourceData.removeAll()
// 如果是图片,直接从缓存中加载
if self.currentResourceType == .compressPhoto {
......@@ -367,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")
}
}
......@@ -323,6 +323,7 @@ extension HomePhotosDetailViewController:WaterfallMutiSectionDelegate,UICollecti
}
if let view = self.videoDetailNavView {
cell.selectImageView.isHidden = !view.startSelectButton.isSelected
cell.extensionView.isHidden = !view.startSelectButton.isSelected
}
cell.cellCallBack = {[weak self] ident,order in
......
......@@ -249,6 +249,7 @@ extension HomeVideoDetailController:WaterfallMutiSectionDelegate,UICollectionVie
if let view = self.videoDetailNavView {
cell.selectImageView.isHidden = !view.startSelectButton.isSelected
cell.extensionView.isHidden = !view.startSelectButton.isSelected
}
cell.clickCallBack = {[weak self] click,order in
......
......@@ -11,7 +11,15 @@ import Photos
class PhotoRemoveViewController: BaseViewController {
var mediaType : TrashTypeEnum?
var mediaType : TrashTypeEnum? {
didSet{
if mediaType == .video {
self.trashSubView.tipLabel.text = "The video in the trash can"
}else {
self.trashSubView.tipLabel.text = "The photo in the trash can"
}
}
}
private var bottomConstraint: Constraint?
......@@ -96,14 +104,23 @@ class PhotoRemoveViewController: BaseViewController {
self.showCurrentPageUIWhenTashDataChanged()
}
}
self.trashSubView.presentTashDetailViewClickCallBack = {
self.trashSubView.presentTashDetailViewClickCallBack = {[weak self] in
guard let self else {return}
// 进入垃圾桶详情页面
DispatchQueue.main.async {
let vc : TrashViewController = TrashViewController()
vc.currentPage = self.getJumpPageIndex()
vc.dissmisCallBack = {[weak self] in
guard let self else {return}
self.showCurrentPageUIWhenTashDataChanged()
}
self.present(vc, animated: true)
}
}
self.navView.reSetCallBack = {
if self.currentIndex > 0 {
......@@ -122,6 +139,51 @@ class PhotoRemoveViewController: BaseViewController {
}
showCurrentPageUIWhenTashDataChanged()
self.addListener()
}
// 跳转垃圾桶第几个page
func getJumpPageIndex()->Int{
if self.mediaType == .video {
return 1
}
if self.mediaType == .other {
return 2
}
if self.mediaType == .shot{
return 3
}
return 1
}
private func addListener(){
NotificationCenter.default.addObserver(forName: TrashDefaultView.jumpToPhotosDetailPageName, object: nil, queue: .main) { [weak self] notification in
guard let self = self,
let type = notification.userInfo?["type"] as? String else { return }
if let targetVC = self.navigationController?.viewControllers.first(where: { $0 is HomeViewController }) as? HomeViewController {
self.navigationController?.popToViewController(targetVC, animated: false)
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()
targetVC.navigationController?.pushViewController(vc, animated: true)
}
}
}
NotificationCenter.default.addObserver(forName: TrashDefaultView.jumpToVideosDetailPageName, object: nil, queue: .main) { [weak self] notification in
guard let self else {return}
if let targetVC = self.navigationController?.viewControllers.first(where: { $0 is HomeViewController }) as? HomeViewController {
self.navigationController?.popToViewController(targetVC, animated: false)
PhotoDataManager.manager.loadFromFileSystem { model in
let data = model.otherModelArray[0]
let vc:HomeVideoDetailController = HomeVideoDetailController(model: data)
vc.dealData()
targetVC.navigationController?.pushViewController(vc, animated: true)
}
}
}
}
private func removeCurrentMediaTypeTrashLastData() {
// 移除单利中当前类型最后一个数据
......@@ -278,7 +340,7 @@ class PhotoRemoveViewController: BaseViewController {
for item in dataSg{
// 如果是视频的话不会进行分组,所以每次传入不同的id组
let uinqueId = UUID().uuidString
let success = GroupDatabase.shared.insert(localIdentifier: item.localIdentifier, assetSize: item.assetSize, createDate: item.createDate, mediaType: type.dbType,groupId: uinqueId)
let success = GroupDatabase.shared.insert(localIdentifier: item.localIdentifier, assetSize: item.assetSize, createDate: item.createDate, mediaType: type == TrashTypeEnum.video ? 2 : 1,groupId: uinqueId)
if success == false {
Print("保留单利数据到数据库保留列表失败")
}
......@@ -468,4 +530,17 @@ class PhotoRemoveViewController: BaseViewController {
}
}
}
@objc func popCurrentPage(){
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
......@@ -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)
......
......@@ -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,12 +19,17 @@ 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()
}
......@@ -34,14 +41,20 @@ class TrashViewController: UIViewController {
if view.isKind(of: TrashContenView.self){
let tempView = view as! TrashContenView
if tempView.trashType == item{
tempView.mediaType = item
tempView.dataSource = data
DispatchQueue.main.async {
tempView.collectionView.reloadData()
}
}
}
}
// 将所有数据获取完成之后,重新刷新下删除按钮
setDelButtonUI()
}
// 在第一次进去的时候执行一次
private func setContentOffSet(){
self.contentScrollView.contentOffset = CGPointMake(ScreenW * CGFloat(self.currentPage - 1), 0)
}
......@@ -62,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
......@@ -85,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() {
......@@ -191,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()
}
}
......@@ -9,40 +9,58 @@ 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()
clearCurrentMediaTypeTrashDBData()
clearCurrentMediaTypeTrashSigtonData(mediaType: mediaType)
clearCurrentMediaTypeTrashDBData(mediaType: mediaType)
// 删除完成之后回调
comlete()
}
}else{
comlete()
}
}
func clearCurrentMediaTypeTrashSigtonData(){
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("删除单利中当前垃圾桶数据成功")
}
}
func clearCurrentMediaTypeTrashDBData(){
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 {
if !success {
Print("删除数据库当前垃圾桶数据失败")
}
}else{
let success = TrashDatabase.shared.delete(localIdentifier: identifier!)
if !success {
Print("删除数据库当前数据失败")
}
}
}
}
......
......@@ -7,8 +7,11 @@
import Foundation
enum TrashTypeEnum : CaseIterable{
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())
}
}
}
......@@ -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 }) {
......
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