Commit aa8c57c4 authored by shenyong's avatar shenyong

1.启动内购页面UI重写

2.功能引导页完成
3.本地通知功能
4.自定义评价弹窗
5.删除完成庆祝页
parent 4abddffe
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "img_dian_guide@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "img_dian_guide@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "img_file_guide@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "img_file_guide@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "tu1@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "tu1@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "tu2@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "tu2@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "photos@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "photos@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "tu3@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "tu3@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "tu4@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "tu4@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "sreenshot@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "sreenshot@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "video@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "video@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon2@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon2@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon1@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon1@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "pingjiatu@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "pingjiatu@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -299,6 +299,7 @@ class CompressCompletedViewController : BaseViewController{
var count = 0
for data in self.model! {
let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [data.ident], options: nil)
let fileSize = FileTool().calculateTotalAssetSize(fetchResult: fetchResult)
let assetToDelete = fetchResult.firstObject
PHPhotoLibrary.shared().performChanges ({
PHAssetChangeRequest.deleteAssets([assetToDelete] as NSFastEnumeration)
......@@ -308,6 +309,7 @@ class CompressCompletedViewController : BaseViewController{
let deleteModel = AssetModel(localIdentifier: data.ident, assetSize: data.orgSize, createDate: data.createDate)
PhotoDataManager.manager.removeDataWhenDeleteInPage(data: [deleteModel])
print("删除文件成功")
self.showDeleteSuccess(fileCount:1, fileSize: fileSize)
}else {
if let error = error {
print("删除文件时出错: \(error.localizedDescription)")
......@@ -321,6 +323,18 @@ class CompressCompletedViewController : BaseViewController{
}
}
// 删除成功页面
func showDeleteSuccess(fileCount:Int,fileSize:Int64){
DispatchQueue.main.async {
let vc = DelSuccessViewController()
vc.delType = fileCount > 1 ? "photos" : "photo"
vc.fileSzie = fileSize
vc.fileCount = fileCount
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true)
}
}
func jumpToCompressVC(){
DispatchQueue.main.async {
if let targetVC = self.navigationController?.viewControllers.first(where: { $0 is CompressController }) {
......
......@@ -6,6 +6,7 @@
//
import UIKit
import Photos
class HomeInfoViewController:BaseViewController {
......@@ -61,14 +62,18 @@ class HomeInfoViewController:BaseViewController {
for item in imgs {
tempStringArray.append(item.localIdentifier)
}
PhotoAndVideoMananger.deleteAssets(localIdentifiers: tempStringArray) {[weak self] in
let fetchs = PHAsset.fetchAssets(withLocalIdentifiers: tempStringArray, options: nil)
let fileSize = FileTool().calculateTotalAssetSize(fetchResult: fetchs)
PhotoAndVideoMananger.deleteAssets(localIdentifiers: tempStringArray) {[weak self] in
guard let self else {return}
// 更新免费次数
if isAfterAdv == false {
updateFreeTimes()
}
self.showDeleteSuccess(fileCount: tempStringArray.count, fileSize: fileSize)
// 删除缓存数据
PhotoDataManager.manager.removeDataWhenDeleteInPage(data: imgs)
......@@ -245,7 +250,67 @@ class HomeInfoViewController:BaseViewController {
super.viewDidLoad()
titleView.model.title = ""
showTipsVC()
}
// 删除成功页面
func showDeleteSuccess(fileCount:Int,fileSize:Int64){
DispatchQueue.main.async {
let vc = DelSuccessViewController()
vc.delType = fileCount > 1 ? "photos" : "photo"
vc.fileSzie = fileSize
vc.fileCount = fileCount
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true)
}
}
func showTipsVC(){
guard let type = self.type else{
return
}
guard TipsManager.getLoadStauts(type) == false else{
return
}
switch type {
case .duplicates:
let vc = TipsDuplicatesSimilarController()
vc.type = .duplicates
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true)
case .similar:
let vc = TipsDuplicatesSimilarController()
vc.type = .similar
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true)
case .videos:
let vc = TipsVideoPhotoController()
vc.type = .video
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true)
case .similarScreenshots:
break
case .screenshots:
let vc = TipsScreenShotController()
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true)
case .SimilarVideos:
break
case .Other:
let vc = TipsVideoPhotoController()
vc.type = .photo
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true)
}
TipsManager.saveLoadStatus(type)
}
override func addViews() {
......
......@@ -191,7 +191,31 @@ class HomePhotosDetailViewController : BaseViewController {
// 添加垃圾桶功能
setUI()
showTipsVC()
}
func showTipsVC(){
guard let mediaType = mediaType else{
return
}
guard TipsManager.getLoadStauts(mediaType) == false else{
return
}
if mediaType == .screenshots{
let vc = TipsScreenShotController()
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true)
}else{
let vc = TipsVideoPhotoController()
vc.type = .photo
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true)
}
TipsManager.saveLoadStatus(mediaType)
}
override func viewWillAppear(_ animated: Bool) {
......@@ -509,6 +533,7 @@ extension HomePhotosDetailViewController:WaterfallMutiSectionDelegate,UICollecti
self.selectedModel = tempArray
let allIdent = self.selectedModel.map{ $0.localIdentifier }
let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: allIdent, options: nil)
let fileSize = FileTool().calculateTotalAssetSize(fetchResult: fetchResult)
PHPhotoLibrary.shared().performChanges ({
PHAssetChangeRequest.deleteAssets(fetchResult as NSFastEnumeration)
}){ success, error in
......@@ -517,6 +542,7 @@ extension HomePhotosDetailViewController:WaterfallMutiSectionDelegate,UICollecti
if isAfterAdv == false {
self.updateFreeTimes()
}
self.showDeleteSuccess(fileCount: allIdent.count, fileSize: fileSize)
self.updateCurrentPageWhenDeleteAny()
}else {
if let error = error {
......@@ -536,6 +562,18 @@ extension HomePhotosDetailViewController:WaterfallMutiSectionDelegate,UICollecti
}
}
// 删除成功页面
func showDeleteSuccess(fileCount:Int,fileSize:Int64){
DispatchQueue.main.async {
let vc = DelSuccessViewController()
vc.delType = fileCount > 1 ? "photos" : "photo"
vc.fileSzie = fileSize
vc.fileCount = fileCount
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true)
}
}
func updateCurrentPageWhenDeleteAny(){
for item in self.selectedModel {
// 移除当前数据
......
......@@ -161,8 +161,8 @@ class HomeVideoDetailController :BaseViewController {
view.backgroundColor = .white
self.navigationController?.navigationBar.isHidden = true
setUI()
showTipsVC()
}
override func viewWillAppear(_ animated: Bool) {
......@@ -173,6 +173,19 @@ class HomeVideoDetailController :BaseViewController {
self.collectionView.reloadData()
}
}
func showTipsVC(){
guard TipsManager.getLoadStauts(.videos) == false else{
return
}
let vc = TipsVideoPhotoController()
vc.type = .video
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true)
TipsManager.saveLoadStatus(.videos)
}
}
......@@ -440,6 +453,7 @@ extension HomeVideoDetailController:WaterfallMutiSectionDelegate,UICollectionVie
self.selectedModel = tempArray
// 删除当前选择
let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: self.selectedModel.map{$0.localIdentifier}, options: nil)
let fileSize = FileTool().calculateTotalAssetSize(fetchResult: fetchResult)
PHPhotoLibrary.shared().performChanges ({
PHAssetChangeRequest.deleteAssets(fetchResult as NSFastEnumeration)
}){ success, error in
......@@ -448,7 +462,9 @@ extension HomeVideoDetailController:WaterfallMutiSectionDelegate,UICollectionVie
// 如果是看广告删除的,且还有免费次数
if isAfterAdv == false {
self.updateFreeTimes()
}
self.showDeleteSuccess(fileCount: self.selectedModel.count, fileSize: fileSize)
// 移除当前总数据源的数据
self.updateCurrentPageWhenDeleteAny()
}else {
......@@ -460,6 +476,18 @@ extension HomeVideoDetailController:WaterfallMutiSectionDelegate,UICollectionVie
}
}
// 删除成功页面
func showDeleteSuccess(fileCount:Int,fileSize:Int64){
DispatchQueue.main.async {
let vc = DelSuccessViewController()
vc.delType = fileCount > 1 ? "videos" : "video"
vc.fileSzie = fileSize
vc.fileCount = fileCount
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true)
}
}
func setDefaultPage(){
DispatchQueue.main.async {
if self.resourceData.count == 0 {
......
......@@ -95,6 +95,8 @@ class HomeViewController:BaseViewController {
// 调用下追踪权限
checkTrackingAuthorization()
NotificationManager().configNotifications()
let dataUpdated = Notification.Name("DataUpdatedNotification")
NotificationCenter.default.addObserver(self, selector: #selector(handleDataUpdated(_:)), name: dataUpdated, object: nil)
......
//
// HomePayDueView.swift
// PhoneManager
//
// Created by edy on 2025/4/27.
//
import UIKit
import StoreKit
class HomePayDueView: UIView {
@IBOutlet weak var dueDay: UILabel!
@IBOutlet weak var dueTime: UILabel!
@IBOutlet weak var freeTime: UILabel!
@IBOutlet weak var priceL: UILabel!
@IBOutlet weak var saveL: UILabel!
@IBOutlet weak var freePrice: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
freeTime.layer.cornerRadius = 8
freeTime.layer.masksToBounds = true
saveL.layer.cornerRadius = 8
saveL.layer.masksToBounds = true
}
func reloadUI(_ type:Int,product1:SKProduct?,product2:SKProduct?){
guard let pord = product1,let pord1 = product2 else { return }
if type == 0{
// 免费试用订阅
dueDay.text = "Due today"
freeTime.isHidden = false
freePrice.isHidden = false
saveL.isHidden = true
priceL.text = "\(pord.localizedPrice)"
priceL.font = UIFont.systemFont(ofSize: 12, weight: .regular)
dueTime.text = "Due" + getDateSevenDaysLater()
}else{
//永久付费
dueDay.text = "One-time payment"
dueTime.text = "Lifetime"
freeTime.isHidden = true
freePrice.isHidden = true
saveL.isHidden = false
priceL.text = "\(pord1.localizedPrice)"
priceL.font = UIFont.systemFont(ofSize: 14, weight: .regular)
}
}
func getDateSevenDaysLater() -> String {
// 获取今天的日期
let today = Date()
// 创建一个日历实例
let calendar = Calendar.current
// 计算今天的7天后的日期
if let sevenDaysLater = calendar.date(byAdding: .day, value: 7, to: today) {
// 创建一个日期格式化器
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMMM dd, yyyy" // 设置日期格式为 "March 24, 2025"
// 格式化日期并返回字符串
return dateFormatter.string(from: sevenDaysLater)
}
return ""
}
}
This diff is collapsed.
......@@ -112,8 +112,9 @@ extension HomePayViewController {
let p1 = currentProduct?.first,
let p2 = currentProduct?.last
else { return }
homePayView?.product = p1
homePayView?.product1 = p2
homePayView?.reloadSKPorduct(product: p1, product1: p2)
// homePayView?.product = p1
// homePayView?.product1 = p2
}
private func storeKeD() -> Void {
......
//
// CustomRateView.swift
// PhoneManager
//
// Created by edy on 2025/4/27.
//
import UIKit
class CustomRate{
// 自定义初始化方法
func show() {
let view = Bundle.main.loadNibNamed("CustomRateView", owner: nil)?.last as! CustomRateView
view.frame = CGRect.init(x: 0, y: 0, width:ScreenW, height: ScreenH)
view.show()
}
}
class CustomRateView: UIView {
@IBOutlet weak var bgView: UIView!
@IBOutlet weak var okBtn: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
okBtn.layer.cornerRadius = 15
bgView.layer.cornerRadius = 12
}
@IBAction func okClick(_ sender: Any) {
guard let url = URL(string: "https://apps.apple.com/us/app/id\(APPConfig.APPID)?action=write-review") else {
return
}
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
@IBAction func noClick(_ sender: Any) {
self.alpha = 1
UIView.animate(withDuration: 0.2) {
self.alpha = 0
}completion: { _ in
self.removeFromSuperview()
}
}
func show(){
KEYWINDOW()?.addSubview(self)
self.alpha = 0
UIView.animate(withDuration: 0.2) {
self.alpha = 1
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="6qH-5M-OIW" customClass="CustomRateView" customModule="PhoneManager" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="eO0-mO-cv0">
<rect key="frame" x="15" y="235" width="363" height="370"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_rate_simle" translatesAutoresizingMaskIntoConstraints="NO" id="wNX-WZ-HxL">
<rect key="frame" x="112" y="35" width="139" height="139"/>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Hey, do you like PhoneManager?" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ka6-1V-usy">
<rect key="frame" x="16" y="194" width="331" height="24"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="20"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="So, could you please rate us on the App Store?" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="DpR-9g-ROc">
<rect key="frame" x="20" y="224" width="323" height="17"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" red="0.33333334329999997" green="0.33333334329999997" blue="0.33333334329999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1Uo-ks-UkN">
<rect key="frame" x="20" y="257" width="323" height="46"/>
<color key="backgroundColor" red="0.0" green="0.50980392159999999" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="46" id="gvU-wv-6JI"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="16"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="OK"/>
<connections>
<action selector="okClick:" destination="6qH-5M-OIW" eventType="touchUpInside" id="zpI-EB-wAO"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1EK-jZ-7MV">
<rect key="frame" x="126" y="315" width="111" height="32"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="16"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="No, Thank you">
<color key="titleColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="noClick:" destination="6qH-5M-OIW" eventType="touchUpInside" id="j6b-pb-M6h"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="ka6-1V-usy" firstAttribute="leading" secondItem="eO0-mO-cv0" secondAttribute="leading" constant="16" id="0A6-BH-8Sr"/>
<constraint firstItem="1EK-jZ-7MV" firstAttribute="top" secondItem="1Uo-ks-UkN" secondAttribute="bottom" constant="12" id="3SY-W0-oUX"/>
<constraint firstItem="1Uo-ks-UkN" firstAttribute="leading" secondItem="eO0-mO-cv0" secondAttribute="leading" constant="20" id="8HA-G5-A7A"/>
<constraint firstItem="wNX-WZ-HxL" firstAttribute="top" secondItem="eO0-mO-cv0" secondAttribute="top" constant="35" id="FXq-i7-bhO"/>
<constraint firstItem="DpR-9g-ROc" firstAttribute="top" secondItem="ka6-1V-usy" secondAttribute="bottom" constant="6" id="H6a-Vq-fsl"/>
<constraint firstItem="1EK-jZ-7MV" firstAttribute="centerX" secondItem="1Uo-ks-UkN" secondAttribute="centerX" id="HOi-y2-0I1"/>
<constraint firstAttribute="trailing" secondItem="1Uo-ks-UkN" secondAttribute="trailing" constant="20" id="IbL-jb-AR6"/>
<constraint firstItem="wNX-WZ-HxL" firstAttribute="centerX" secondItem="eO0-mO-cv0" secondAttribute="centerX" id="PA1-8i-rIP"/>
<constraint firstAttribute="trailing" secondItem="DpR-9g-ROc" secondAttribute="trailing" constant="20" id="SA7-0D-qqp"/>
<constraint firstItem="DpR-9g-ROc" firstAttribute="leading" secondItem="eO0-mO-cv0" secondAttribute="leading" constant="20" id="VFt-lh-z7M"/>
<constraint firstItem="1Uo-ks-UkN" firstAttribute="top" secondItem="DpR-9g-ROc" secondAttribute="bottom" constant="16" id="eMT-yH-PKn"/>
<constraint firstItem="ka6-1V-usy" firstAttribute="top" secondItem="wNX-WZ-HxL" secondAttribute="bottom" constant="20" id="gjg-1p-0ZY"/>
<constraint firstAttribute="height" constant="370" id="rQr-Wr-23b"/>
<constraint firstAttribute="trailing" secondItem="ka6-1V-usy" secondAttribute="trailing" constant="16" id="xwQ-Wf-8Rz"/>
</constraints>
</view>
</subviews>
<viewLayoutGuide key="safeArea" id="LTt-HW-XdR"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.5" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="eO0-mO-cv0" firstAttribute="leading" secondItem="LTt-HW-XdR" secondAttribute="leading" constant="15" id="EOH-0c-Twg"/>
<constraint firstItem="LTt-HW-XdR" firstAttribute="trailing" secondItem="eO0-mO-cv0" secondAttribute="trailing" constant="15" id="TZF-xM-ND5"/>
<constraint firstItem="eO0-mO-cv0" firstAttribute="top" secondItem="LTt-HW-XdR" secondAttribute="top" constant="176" id="eZu-0d-lB0"/>
</constraints>
<connections>
<outlet property="bgView" destination="eO0-mO-cv0" id="oYY-Ia-swm"/>
<outlet property="okBtn" destination="1Uo-ks-UkN" id="DHs-t0-6Mz"/>
</connections>
<point key="canvasLocation" x="88" y="-4"/>
</view>
</objects>
<resources>
<image name="icon_rate_simle" width="139" height="139"/>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>
//
// DelSuccessViewController.swift
// PhoneManager
//
// Created by edy on 2025/4/27.
//
import UIKit
import Photos
import Lottie
class DelSuccessViewController: UIViewController {
@IBOutlet weak var bottomBtn: UIButton!
@IBOutlet weak var bottomText: UILabel!
@IBOutlet weak var points: UILabel!
@IBOutlet weak var delText: UILabel!
var delType:String = "photo"
var fileCount:Int = 0
var fileSzie:Int64 = 0
override func viewDidLoad() {
super.viewDidLoad()
bottomBtn.layer.cornerRadius = 15
animationView.frame = CGRect(x: (ScreenW-250)/2+20, y: 80, width: 250, height: 250) // 设置 frame
view.addSubview(animationView)
startAnimation()
reloadUI()
}
lazy var animationView : LottieAnimationView = {
let animationView = LottieAnimationView(name: "delSuccessCelebrate")
animationView.loopMode = .playOnce
return animationView
}()
func startAnimation() {
// 播放动画
animationView.play { [weak self] finished in
// 动画结束后等待 2 秒再重新播放
guard let self = self else { return }
if finished {
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
self.startAnimation() // 重新开始动画
}
}
}
}
@IBAction func closeClick(_ sender: Any) {
self.dismiss(animated: true)
}
func reloadUI(){
let size = fileSzie
points.text = "Save 10 points"
delText.text = "\(fileCount) \(delType)(\(FileTool().formatBytes(size)))"
bottomText.text = "Clear the \"Recently Deleted\" album on iPhone to free up \(FileTool().formatBytes(size)) of space."
// 设置 points 标签
let pointsAttributedString = NSMutableAttributedString(string: "Save 10 points")
let pointsRange = (points.text! as NSString).range(of: "10 points")
pointsAttributedString.addAttribute(.foregroundColor, value:UIColor.colorWithHex(hexStr: "#0082FF"), range: pointsRange)
points.attributedText = pointsAttributedString
// 设置 delText 标签
let formattedSize = FileTool().formatBytes(size)
let delTextString = "\(fileCount) \(delType)(\(formattedSize))"
let delTextAttributedString = NSMutableAttributedString(string: delTextString)
// 设置 fetchResult.count 和 delType 的范围
let countRange = (delTextString as NSString).range(of: "\(fileCount)")
let delTypeRange = (delTextString as NSString).range(of: delType)
// 设置颜色为蓝色
delTextAttributedString.addAttribute(.foregroundColor, value: UIColor.colorWithHex(hexStr: "#0082FF"), range: countRange)
delTextAttributedString.addAttribute(.foregroundColor, value: UIColor.colorWithHex(hexStr: "#0082FF"), range: delTypeRange)
// 其他文本设置为灰色
let otherRange = (delTextString as NSString).range(of: "(\(formattedSize))") // 获取其他文本的范围
delTextAttributedString.addAttribute(.foregroundColor, value: UIColor.colorWithHex(hexStr: "#1A1A1A"), range: otherRange)
delText.attributedText = delTextAttributedString
}
}
//
// GuideTipsModel.swift
// PhoneManager
//
// Created by edy on 2025/4/27.
//
import Foundation
enum GuideTipsType{
case duplicates,similar,video,photo
}
extension GuideTipsType{
var tipOneIcon:String{
switch self {
case .duplicates:
return "tips_duplicates_1"
case .similar:
return "tips_similar_1"
case .video:
return "tips_video"
case .photo:
return "tips_photos"
}
}
var tipTwoIcon:String{
switch self {
case .duplicates:
return "tips_duplicates_2"
case .similar:
return "tips_similar_2"
case .video:
return ""
case .photo:
return ""
}
}
var title:String{
switch self {
case .duplicates:
return "Duplicates"
case .similar:
return "Similar photos"
case .video:
return "Video"
case .photo:
return "Photo"
}
}
var lineText:String{
switch self {
case .duplicates:
return "2 Duplicates"
case .similar:
return "2 Similar"
case .video:
return ""
case .photo:
return ""
}
}
var text:String{
switch self {
case .duplicates:
return "These are duplicate photos - they are completely identical to each other. Immediately delete all duplicate items and keep the original version."
case .similar:
return "We will group similar photos together and provide recommendations for the best photos. Please safely view and select the photos you want to move to the trash can."
case .video:
return "View all of your videos. Sort them by size or date. View the video that occupies the maximum space."
case .photo:
return "These are photos that do not belong to any category. Sort them by size or date. Delete unnecessary photos."
}
}
}
struct TipsManager{
static func saveLoadStatus(_ type:PhotsFileType){
let key = "isFirstShow_\(type.rawValue)"
UserDefaults.standard.set(true, forKey:key)
UserDefaults.standard.synchronize()
}
static func getLoadStauts(_ type:PhotsFileType) ->Bool{
let key = "isFirstShow_\(type.rawValue)"
if let show = UserDefaults.standard.value(forKey: key) as? Bool{
return show
}
return false
}
}
//
// TipsDuplicatesSimilarController.swift
// PhoneManager
//
// Created by edy on 2025/4/27.
//
import UIKit
class TipsDuplicatesSimilarController: UIViewController {
@IBOutlet weak var lineText: UILabel!
@IBOutlet weak var startBtn: UIButton!
@IBOutlet weak var iconOne: UIImageView!
@IBOutlet weak var iconTwo: UIImageView!
@IBOutlet weak var mainTitle: UILabel!
@IBOutlet weak var subTitle: UILabel!
var type:GuideTipsType = .duplicates
override func viewDidLoad() {
super.viewDidLoad()
startBtn.layer.cornerRadius = 15
lineText.text = type.lineText
iconOne.image = UIImage.init(named: type.tipOneIcon)
iconTwo.image = UIImage.init(named: type.tipTwoIcon)
mainTitle.text = type.title
subTitle.text = type.text
}
@IBAction func startClick(_ sender: Any) {
self.dismiss(animated: true)
}
}
//
// TipsScreenShotController.swift
// PhoneManager
//
// Created by edy on 2025/4/27.
//
import UIKit
class TipsScreenShotController: UIViewController {
@IBOutlet weak var startBtn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
startBtn.layer.cornerRadius = 15
}
@IBAction func startClick(_ sender: Any) {
self.dismiss(animated: true)
}
}
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="TipsScreenShotController" customModule="PhoneManager" customModuleProvider="target">
<connections>
<outlet property="startBtn" destination="ybT-3z-Wb2" id="Gl8-Ue-VUX"/>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="430" height="932"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="tips_sreenshot" translatesAutoresizingMaskIntoConstraints="NO" id="QGK-yy-jOF">
<rect key="frame" x="17" y="105" width="396" height="396"/>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ybT-3z-Wb2">
<rect key="frame" x="15" y="822" width="400" height="46"/>
<color key="backgroundColor" red="0.0" green="0.50980392159999999" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="46" id="4Qc-vw-6p8"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="16"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="Let's start now"/>
<connections>
<action selector="startClick:" destination="-1" eventType="touchUpInside" id="1KQ-NC-6EU"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="ScreenShot" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="h8I-lL-ELV">
<rect key="frame" x="146.66666666666666" y="556" width="136.99999999999997" height="30"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="25"/>
<color key="textColor" red="0.1019607843" green="0.1019607843" blue="0.1019607843" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VkR-UI-FBK">
<rect key="frame" x="25" y="604" width="380" height="50.333333333333371"/>
<string key="text">We will group similar screenshots together so that you can view them faster! Please safely view and select the customer cut-off you want to move to the trash can.</string>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<viewLayoutGuide key="safeArea" id="fnl-2z-Ty3"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="fnl-2z-Ty3" firstAttribute="trailing" secondItem="ybT-3z-Wb2" secondAttribute="trailing" constant="15" id="1CR-10-bnb"/>
<constraint firstItem="fnl-2z-Ty3" firstAttribute="trailing" secondItem="VkR-UI-FBK" secondAttribute="trailing" constant="25" id="4JU-rc-hns"/>
<constraint firstItem="QGK-yy-jOF" firstAttribute="leading" secondItem="fnl-2z-Ty3" secondAttribute="leading" constant="17" id="I80-yL-ctf"/>
<constraint firstItem="ybT-3z-Wb2" firstAttribute="leading" secondItem="fnl-2z-Ty3" secondAttribute="leading" constant="15" id="IIW-NA-SQU"/>
<constraint firstItem="fnl-2z-Ty3" firstAttribute="bottom" secondItem="ybT-3z-Wb2" secondAttribute="bottom" constant="30" id="NWj-ld-9UY"/>
<constraint firstItem="VkR-UI-FBK" firstAttribute="top" secondItem="h8I-lL-ELV" secondAttribute="bottom" constant="18" id="aYz-ey-Yc7"/>
<constraint firstItem="QGK-yy-jOF" firstAttribute="top" secondItem="fnl-2z-Ty3" secondAttribute="top" constant="46" id="cHD-Cc-XiT"/>
<constraint firstItem="h8I-lL-ELV" firstAttribute="top" secondItem="QGK-yy-jOF" secondAttribute="bottom" constant="55" id="dDM-Pu-5g1"/>
<constraint firstItem="VkR-UI-FBK" firstAttribute="leading" secondItem="fnl-2z-Ty3" secondAttribute="leading" constant="25" id="qMJ-l4-yde"/>
<constraint firstItem="h8I-lL-ELV" firstAttribute="centerX" secondItem="i5M-Pr-FkT" secondAttribute="centerX" id="qMT-Hi-GFU"/>
<constraint firstItem="fnl-2z-Ty3" firstAttribute="trailing" secondItem="QGK-yy-jOF" secondAttribute="trailing" constant="17" id="tuK-Vk-YgB"/>
</constraints>
<point key="canvasLocation" x="82" y="24"/>
</view>
</objects>
<resources>
<image name="tips_sreenshot" width="341" height="396"/>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>
//
// TipsVideoPhotoController.swift
// PhoneManager
//
// Created by edy on 2025/4/27.
//
import UIKit
class TipsVideoPhotoController: UIViewController {
@IBOutlet weak var startBtn: UIButton!
@IBOutlet weak var mainTitle: UILabel!
@IBOutlet weak var icon: UIImageView!
@IBOutlet weak var subtitle: UILabel!
var type:GuideTipsType = .duplicates
override func viewDidLoad() {
super.viewDidLoad()
startBtn.layer.cornerRadius = 15
icon.image = UIImage.init(named: type.tipOneIcon)
mainTitle.text = type.title
subtitle.text = type.text
}
@IBAction func startClick(_ sender: Any) {
self.dismiss(animated: true)
}
}
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="TipsVideoPhotoController" customModule="PhoneManager" customModuleProvider="target">
<connections>
<outlet property="icon" destination="OpM-5k-AoO" id="Y63-tI-7xy"/>
<outlet property="mainTitle" destination="dcU-ld-SUp" id="ngg-L4-U4l"/>
<outlet property="startBtn" destination="KHL-A7-P8d" id="qKH-gd-iBZ"/>
<outlet property="subtitle" destination="LeZ-aM-wDE" id="SaD-9Z-sOc"/>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="tips_photos" translatesAutoresizingMaskIntoConstraints="NO" id="OpM-5k-AoO">
<rect key="frame" x="65.666666666666686" y="169" width="262" height="306"/>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Other photos" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dcU-ld-SUp">
<rect key="frame" x="119.66666666666667" y="555" width="153.66666666666663" height="30"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="25"/>
<color key="textColor" red="0.1019607843" green="0.1019607843" blue="0.1019607843" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="These are photos that do not belong to any category. Sort them by size or date. Delete unnecessary photos." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="LeZ-aM-wDE">
<rect key="frame" x="25" y="603" width="343" height="50.333333333333371"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="KHL-A7-P8d">
<rect key="frame" x="15" y="742" width="363" height="46"/>
<color key="backgroundColor" red="0.0" green="0.50980392159999999" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="46" id="bCa-jh-IT1"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="16"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="Let's start now"/>
<connections>
<action selector="startClick:" destination="-1" eventType="touchUpInside" id="66j-JZ-X0u"/>
</connections>
</button>
</subviews>
<viewLayoutGuide key="safeArea" id="fnl-2z-Ty3"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="OpM-5k-AoO" firstAttribute="centerX" secondItem="i5M-Pr-FkT" secondAttribute="centerX" id="0N1-Db-xJx"/>
<constraint firstItem="OpM-5k-AoO" firstAttribute="top" secondItem="fnl-2z-Ty3" secondAttribute="top" constant="110" id="BQx-h1-esb"/>
<constraint firstAttribute="trailing" secondItem="KHL-A7-P8d" secondAttribute="trailing" constant="15" id="MaN-MT-FnY"/>
<constraint firstItem="dcU-ld-SUp" firstAttribute="top" secondItem="OpM-5k-AoO" secondAttribute="bottom" constant="80" id="Okf-QC-Jrn"/>
<constraint firstItem="KHL-A7-P8d" firstAttribute="leading" secondItem="fnl-2z-Ty3" secondAttribute="leading" constant="15" id="Rl4-Mh-CiF"/>
<constraint firstItem="fnl-2z-Ty3" firstAttribute="bottom" secondItem="KHL-A7-P8d" secondAttribute="bottom" constant="30" id="SCz-tZ-pgH"/>
<constraint firstItem="dcU-ld-SUp" firstAttribute="centerX" secondItem="i5M-Pr-FkT" secondAttribute="centerX" id="USR-RC-nO5"/>
<constraint firstItem="LeZ-aM-wDE" firstAttribute="top" secondItem="dcU-ld-SUp" secondAttribute="bottom" constant="18" id="Zga-Sv-DYE"/>
<constraint firstItem="fnl-2z-Ty3" firstAttribute="trailing" secondItem="LeZ-aM-wDE" secondAttribute="trailing" constant="25" id="lAh-or-e83"/>
<constraint firstItem="LeZ-aM-wDE" firstAttribute="leading" secondItem="fnl-2z-Ty3" secondAttribute="leading" constant="25" id="plq-KA-cxw"/>
</constraints>
<point key="canvasLocation" x="22" y="20"/>
</view>
</objects>
<resources>
<image name="tips_photos" width="262" height="306"/>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>
//
// FileManager.swift
// PhoneManager
//
// Created by edy on 2025/4/27.
//
import Foundation
import Photos
struct FileTool{
enum StorageType{
case total,used,free
}
func getStorageInfo(for type: StorageType) -> Int64? {
let fileManager = FileManager.default
do {
// 获取主目录的文件系统属性
let systemAttributes = try fileManager.attributesOfFileSystem(forPath: NSHomeDirectory())
// 获取总存储和可用空间
guard let totalSize = systemAttributes[FileAttributeKey.systemSize] as? NSNumber,
let freeSize = systemAttributes[FileAttributeKey.systemFreeSize] as? NSNumber else {
return nil // 返回 nil 如果无法获取所需信息
}
switch type {
case .total:
return totalSize.int64Value // 返回总存储
case .free:
return freeSize.int64Value // 返回可用存储
case .used:
let usedSize = totalSize.int64Value - freeSize.int64Value // 计算已用存储
return usedSize
}
} catch {
print("Error retrieving storage info: \(error)")
return nil
}
}
func formatBytes(_ bytes: Int64) -> String {
let formatter = ByteCountFormatter()
formatter.allowedUnits = [.useKB, .useMB, .useGB] // 选择合适的单位
formatter.countStyle = .file
return formatter.string(fromByteCount: bytes)
}
func calculateTotalAssetSize(fetchResult: PHFetchResult<PHAsset>) -> Int64 {
var totalSize: Int64 = 0
fetchResult.enumerateObjects { (asset, index, stop) in
let resources = PHAssetResource.assetResources(for: asset)
print("当前 asset: \(asset)")
print("资源数量: \(resources.count)")
for resource in resources {
print("资源类型: \(resource.type)")
if let fileSize = resource.value(forKey: "fileSize") as? Int64 {
print("文件大小: \(fileSize)")
totalSize += fileSize
} else {
print("无法获取文件大小")
}
}
}
return totalSize
}
}
import UserNotifications
class NotificationManager {
func configNotifications(){
if let secondlaunch = UserDefaults.standard.value(forKey: "notifications_is_secondlaunch") as? Bool{
if secondlaunch{
Print("二次启动,获取通知权限")
scheduleLocalNotifications()
}
}else{
UserDefaults.standard.setValue(true, forKey: "notifications_is_secondlaunch")
UserDefaults.standard.synchronize()
Print("启动第一次,记录下")
}
}
// 调度本地通知
func scheduleLocalNotifications() {
// 请求通知权限
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { granted, error in
if granted {
// 创建并调度通知
// self.createNotifications()
self.scheduleNotificationsEveryTwoMinutes()
} else {
print("通知权限未被授予: \(String(describing: error))")
}
}
}
// 创建并调度通知
private func createNotifications() {
let content = UNMutableNotificationContent()
content.title = NotificationManagerData().getShowTitle()
content.body = NotificationManagerData().getAlertSubtitle()
content.sound = UNNotificationSound.default
// 定义每天的通知时间
let notificationTimes = [
(hour: 9, minute: 0), // 早上 9 点
(hour: 15, minute: 0), // 下午 3 点
(hour: 21, minute: 0) // 晚上 9 点
]
// 创建通知
for time in notificationTimes {
var dateComponents = DateComponents()
dateComponents.hour = time.hour
dateComponents.minute = time.minute
// 设置触发条件为每天
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
// 创建请求
let request = UNNotificationRequest(identifier: "\(time.hour)-\(time.minute)", content: content, trigger: trigger)
// 添加请求到通知中心
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("添加通知请求时出错: \(error)")
}
}
}
}
// 每 2 分钟调度一次通知
func scheduleNotificationsEveryTwoMinutes() {
let content = UNMutableNotificationContent()
content.title = NotificationManagerData().getShowTitle()
content.body = NotificationManagerData().getAlertSubtitle()
content.sound = UNNotificationSound.default
// 创建触发条件为每 2 分钟
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 600, repeats: true)
// 创建请求
let request = UNNotificationRequest(identifier: "twoMinuteNotification", content: content, trigger: trigger)
// 添加请求到通知中心
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("添加每 2 分钟通知请求时出错: \(error)")
}
}
}
// 取消所有通知
func cancelAllLocalNotifications() {
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
print("所有本地通知已被取消")
}
}
struct NotificationManagerData{
func getShowTitle() ->String{
return "You can free up more storage space!"
}
func getAlertSubtitle() ->String{
return "100 videos and 100 photos are waiting in the \"video&others\" section."
}
}
......@@ -664,6 +664,7 @@ class PhotoAndVideoMananger {
}
}
}
// MARK: - 新方法处理相似图片向下-------------------------------------------------------
......
//
// APPConfig.swift
// PhoneManager
//
// Created by edy on 2025/4/27.
//
import Foundation
struct APPConfig{
static let APPID = "1530333201"
}
func KEYWINDOW() ->UIWindow?{
return UIApplication.shared.windows.first{$0.isKeyWindow}
}
......@@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.app.phonemanager</string>
......
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