Commit 59244d26 authored by edy's avatar edy

photomanager

parent 586f057f
...@@ -7,13 +7,13 @@ ...@@ -7,13 +7,13 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
8767D08846C136C74D7A38AD /* Pods_PhoneManager.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB0D368334BECAFE6594C5F7 /* Pods_PhoneManager.framework */; }; 3A00E856852A8783E544CD7D /* Pods_PhoneManager.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6028F60B696E2F97EAA2325C /* Pods_PhoneManager.framework */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
B29A159C241A6A66EFE07177 /* Pods-PhoneManager.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PhoneManager.debug.xcconfig"; path = "Target Support Files/Pods-PhoneManager/Pods-PhoneManager.debug.xcconfig"; sourceTree = "<group>"; }; 295785B9009F20AC4C1C36C4 /* Pods-PhoneManager.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PhoneManager.debug.xcconfig"; path = "Target Support Files/Pods-PhoneManager/Pods-PhoneManager.debug.xcconfig"; sourceTree = "<group>"; };
CC121720387E40164142F3E2 /* Pods-PhoneManager.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PhoneManager.release.xcconfig"; path = "Target Support Files/Pods-PhoneManager/Pods-PhoneManager.release.xcconfig"; sourceTree = "<group>"; }; 6028F60B696E2F97EAA2325C /* Pods_PhoneManager.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PhoneManager.framework; sourceTree = BUILT_PRODUCTS_DIR; };
DB0D368334BECAFE6594C5F7 /* Pods_PhoneManager.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PhoneManager.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8937DC9D81CEDE823C329A80 /* Pods-PhoneManager.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PhoneManager.release.xcconfig"; path = "Target Support Files/Pods-PhoneManager/Pods-PhoneManager.release.xcconfig"; sourceTree = "<group>"; };
EB388E5B2D8A61A800629B0D /* PhoneManager.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PhoneManager.app; sourceTree = BUILT_PRODUCTS_DIR; }; EB388E5B2D8A61A800629B0D /* PhoneManager.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PhoneManager.app; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */ /* End PBXFileReference section */
...@@ -43,17 +43,17 @@ ...@@ -43,17 +43,17 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
8767D08846C136C74D7A38AD /* Pods_PhoneManager.framework in Frameworks */, 3A00E856852A8783E544CD7D /* Pods_PhoneManager.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
1658DB6C9F7C23ADB69D9D4C /* Frameworks */ = { 27ECDADD9059AB5043B8E1E9 /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DB0D368334BECAFE6594C5F7 /* Pods_PhoneManager.framework */, 6028F60B696E2F97EAA2325C /* Pods_PhoneManager.framework */,
); );
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -61,8 +61,8 @@ ...@@ -61,8 +61,8 @@
CB2ACD1E9442B4500087E831 /* Pods */ = { CB2ACD1E9442B4500087E831 /* Pods */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
B29A159C241A6A66EFE07177 /* Pods-PhoneManager.debug.xcconfig */, 295785B9009F20AC4C1C36C4 /* Pods-PhoneManager.debug.xcconfig */,
CC121720387E40164142F3E2 /* Pods-PhoneManager.release.xcconfig */, 8937DC9D81CEDE823C329A80 /* Pods-PhoneManager.release.xcconfig */,
); );
path = Pods; path = Pods;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -73,7 +73,7 @@ ...@@ -73,7 +73,7 @@
EB388E5D2D8A61A800629B0D /* PhoneManager */, EB388E5D2D8A61A800629B0D /* PhoneManager */,
EB388E5C2D8A61A800629B0D /* Products */, EB388E5C2D8A61A800629B0D /* Products */,
CB2ACD1E9442B4500087E831 /* Pods */, CB2ACD1E9442B4500087E831 /* Pods */,
1658DB6C9F7C23ADB69D9D4C /* Frameworks */, 27ECDADD9059AB5043B8E1E9 /* Frameworks */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
...@@ -92,11 +92,11 @@ ...@@ -92,11 +92,11 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = EB388E6E2D8A61AA00629B0D /* Build configuration list for PBXNativeTarget "PhoneManager" */; buildConfigurationList = EB388E6E2D8A61AA00629B0D /* Build configuration list for PBXNativeTarget "PhoneManager" */;
buildPhases = ( buildPhases = (
0EB1FE5C41C26445102D1AF8 /* [CP] Check Pods Manifest.lock */, 594FD43819933850E07C4C9C /* [CP] Check Pods Manifest.lock */,
EB388E572D8A61A800629B0D /* Sources */, EB388E572D8A61A800629B0D /* Sources */,
EB388E582D8A61A800629B0D /* Frameworks */, EB388E582D8A61A800629B0D /* Frameworks */,
EB388E592D8A61A800629B0D /* Resources */, EB388E592D8A61A800629B0D /* Resources */,
5B498BF73901C867A61ED7C3 /* [CP] Embed Pods Frameworks */, B06228D82143041809F900CE /* [CP] Embed Pods Frameworks */,
); );
buildRules = ( buildRules = (
); );
...@@ -122,6 +122,7 @@ ...@@ -122,6 +122,7 @@
TargetAttributes = { TargetAttributes = {
EB388E5A2D8A61A800629B0D = { EB388E5A2D8A61A800629B0D = {
CreatedOnToolsVersion = 16.2; CreatedOnToolsVersion = 16.2;
LastSwiftMigration = 1620;
}; };
}; };
}; };
...@@ -155,7 +156,7 @@ ...@@ -155,7 +156,7 @@
/* End PBXResourcesBuildPhase section */ /* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */
0EB1FE5C41C26445102D1AF8 /* [CP] Check Pods Manifest.lock */ = { 594FD43819933850E07C4C9C /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
...@@ -177,7 +178,7 @@ ...@@ -177,7 +178,7 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
5B498BF73901C867A61ED7C3 /* [CP] Embed Pods Frameworks */ = { B06228D82143041809F900CE /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
...@@ -209,15 +210,21 @@ ...@@ -209,15 +210,21 @@
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
EB388E6F2D8A61AA00629B0D /* Debug */ = { EB388E6F2D8A61AA00629B0D /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = B29A159C241A6A66EFE07177 /* Pods-PhoneManager.debug.xcconfig */; baseConfigurationReference = 295785B9009F20AC4C1C36C4 /* Pods-PhoneManager.debug.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 4D62BC7QXG; DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 6K23946NQ5;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = PhoneManager/Info.plist; INFOPLIST_FILE = PhoneManager/Info.plist;
INFOPLIST_KEY_NSLocalNetworkUsageDescription = "We need to access the network to load content";
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "We need album permission to access image storage information";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UIMainStoryboardFile = Main;
...@@ -229,9 +236,13 @@ ...@@ -229,9 +236,13 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.app.phonemanager.PhoneManager; PRODUCT_BUNDLE_IDENTIFIER = com.app.phonemanager;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = phonemanager_dev;
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "PhoneManager/Class/Tool/Class/OC/PhoneManager-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
}; };
...@@ -239,15 +250,21 @@ ...@@ -239,15 +250,21 @@
}; };
EB388E702D8A61AA00629B0D /* Release */ = { EB388E702D8A61AA00629B0D /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = CC121720387E40164142F3E2 /* Pods-PhoneManager.release.xcconfig */; baseConfigurationReference = 8937DC9D81CEDE823C329A80 /* Pods-PhoneManager.release.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 4D62BC7QXG; DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 6K23946NQ5;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = PhoneManager/Info.plist; INFOPLIST_FILE = PhoneManager/Info.plist;
INFOPLIST_KEY_NSLocalNetworkUsageDescription = "We need to access the network to load content";
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "We need album permission to access image storage information";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UIMainStoryboardFile = Main;
...@@ -259,9 +276,12 @@ ...@@ -259,9 +276,12 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.app.phonemanager.PhoneManager; PRODUCT_BUNDLE_IDENTIFIER = com.app.phonemanager;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = phonemanager_dev;
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "PhoneManager/Class/Tool/Class/OC/PhoneManager-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
}; };
...@@ -312,6 +332,7 @@ ...@@ -312,6 +332,7 @@
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1", "DEBUG=1",
"$(inherited)", "$(inherited)",
"CV_NO_OBJC_KEYWORDS=1",
); );
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
...@@ -370,6 +391,7 @@ ...@@ -370,6 +391,7 @@
ENABLE_USER_SCRIPT_SANDBOXING = NO; ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu17; GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = "CV_NO_OBJC_KEYWORDS=1";
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNDECLARED_SELECTOR = YES;
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
// //
import UIKit import UIKit
import AppIntents
@main @main
class AppDelegate: UIResponder, UIApplicationDelegate { class AppDelegate: UIResponder, UIApplicationDelegate {
...@@ -13,7 +14,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ...@@ -13,7 +14,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow? var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
setupDefault() setupDefault()
window = UIWindow(frame: UIScreen.main.bounds) window = UIWindow(frame: UIScreen.main.bounds)
...@@ -33,8 +33,24 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ...@@ -33,8 +33,24 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
} }
private func setupDefault() { private func setupDefault() {
NetStatusManager.manager.startNet { status in
switch status {
case .NoNet:
break
case .WIFI,.WWAN:
if (PhotoAndVideoMananger.mananger.allAssets.count == 0) {
PhotoAndVideoMananger.mananger.setAssets()
}
}
}
if #available(iOS 13.0, *) { if #available(iOS 13.0, *) {
let window = UIApplication.shared.windows.first let window = UIApplication.shared.windows.first
...@@ -52,7 +68,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ...@@ -52,7 +68,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
if #available(iOS 11.0, *) { if #available(iOS 11.0, *) {
safeHeight = UIApplication.shared.delegate?.window??.safeAreaInsets.bottom ?? 0 safeHeight = UIApplication.shared.delegate?.window??.safeAreaInsets.bottom ?? 0
}else { }else {
safeHeight = 0 safeHeight = 0
......
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "IMG_0944.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "IMG_0982.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "IMG_0983.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "IMG_0984.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "IMG_0985 1.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "ic_collect_com.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ic_collect_com@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ic_collect_com@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "ic_details_charging.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ic_details_charging@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ic_details_charging@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "ic_sel_com.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ic_sel_com@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ic_sel_com@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "ic_unsel_com.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ic_unsel_com@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ic_unsel_com@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "ic_check_similar.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ic_check_similar@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ic_check_similar@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "ic_close_similar.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ic_close_similar@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ic_close_similar@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "icon_left_setting.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon_left_setting@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon_left_setting@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
//
// ChargeInfoViewController.swift
// PhoneManager
//
// Created by edy on 2025/3/26.
//
import UIKit
class ChargeInfoViewController:BaseViewController {
enum ChargeInfoType {
case setting
case charge
}
var model:ChargeViewCollectionModel?
var type:ChargeInfoType?
var isShowBack:Bool? {
didSet {
changeTitleView()
}
}
var isShowSettingView:Bool? {
didSet {
changeSettingViews()
}
}
lazy var backImageView:ChargeInfoBackView = {
let sview:ChargeInfoBackView = ChargeInfoBackView(frame: view.bounds, backImage: model?.CoverImage ?? "")
return sview
}()
lazy var settingView:ChargeInfoSettingView = {
let sview:ChargeInfoSettingView = ChargeInfoSettingView(frame: CGRect(x: 0, y: 0, width: view.width, height: 78 + safeHeight))
sview.isHidden = type == .setting ? false : true
return sview
}()
init(model: ChargeViewCollectionModel?,type:ChargeInfoType?) {
self.type = type
self.model = model
super.init(nibName: nil, bundle: nil)
}
// 由于继承自 UIViewController,必须实现这个必需的构造器
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override func viewDidLoad() {
super.viewDidLoad()
self.barHidden = true
titleView.model.title = ""
titleView.lineView.isHidden = true
titleView.backgroundColor = .clear
if type == .charge {
DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: {[weak self] in
guard let self else {return}
isShowBack = true
})
}
}
override func addViews() {
view.addSubview(backImageView)
view.addSubview(titleView)
view.addSubview(settingView)
}
func changeSettingViews() {
if type == .charge {
return
}
DispatchQueue.main.async {
if !(self.isShowSettingView ?? false) {
self.settingView.isHidden = false
}
UIView.animate(withDuration: 0.3, animations: {[weak self] in
guard let self else {return}
self.settingView.alpha = (self.isShowSettingView ?? false) ? 0 : 1
}, completion: {[weak self] _ in
guard let self else {return}
self.settingView.isHidden = self.isShowSettingView ?? false
})
}
}
func changeTitleView() {
if !(self.isShowBack ?? false) {
self.titleView.isHidden = false
}
UIView.animate(withDuration: 0.3, animations: {[weak self] in
guard let self else {return}
self.titleView.alpha = (self.isShowBack ?? false) ? 0 : 1
}, completion: {[weak self] _ in
guard let self else {return}
self.titleView.isHidden = self.isShowBack ?? false
})
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
backImageView.snp.makeConstraints { make in
make.center.width.height.equalToSuperview()
}
settingView.snp.makeConstraints { make in
make.bottom.width.centerX.equalToSuperview()
make.height.equalTo(safeHeight + (safeHeight == 0 ? 78 : 68))
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
isShowSettingView = !(isShowSettingView ?? false)
isShowBack = !(isShowBack ?? false)
}
}
//
// ChargeViewController.swift
// PhoneManager
//
// Created by edy on 2025/3/26.
//
import UIKit
class ChargeViewController:BaseViewController {
lazy var detailsBtn:UIButton = {
let sview:UIButton = UIButton()
sview.setImage(UIImage(named: "ic_details_charging"), for: .normal)
sview.width = 20
sview.height = 20
sview.x = view.width - sview.width - 15
sview.centerY = navCenterY
return sview
}()
lazy var chargeView:ChargeView = {
let cY:CGFloat = titleView.height + titleView.y
let sview:ChargeView = ChargeView(frame: CGRect(x: 0, y: cY, width: view.width, height: view.height - cY))
sview.callBack = {[weak self] model in
guard let self else {return}
DispatchQueue.main.async {[weak self] in
guard let self else {return}
if let cModel = model as? ChargeViewCollectionModel {
let vc:ChargeInfoViewController = ChargeInfoViewController(model: cModel, type: ChargeInfoViewController.ChargeInfoType.setting)
self.navigationController?.pushViewController(vc, animated: true)
}
}
}
return sview
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
titleView.addSubview(detailsBtn)
view.addSubview(chargeView)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.barHidden = false
}
}
[
{
"isFree": true,
"CoverImage": "IMG_0985",
"filePath":"",
},
{
"isFree": true,
"CoverImage": "IMG_0984",
"filePath":"",
},
{
"isFree": false,
"CoverImage": "IMG_0983",
"filePath":"",
},
{
"isFree": false,
"CoverImage": "IMG_0982",
"filePath":"",
},
{
"isFree": false,
"CoverImage": "IMG_0944",
"filePath":"",
},
{
"isFree": false,
"CoverImage": "IMG_0985",
"filePath":"",
},
{
"isFree": false,
"CoverImage": "IMG_0984",
"filePath":"",
},
{
"isFree": false,
"CoverImage": "IMG_0983",
"filePath":"",
},
{
"isFree": false,
"CoverImage": "IMG_0982",
"filePath":"",
},
]
//
// HomeTitleCollectionModel.swift
// PhoneManager
//
// Created by edy on 2025/3/26.
//
import Foundation
func loadChargeImtesSONFromBundle() -> [ChargeViewCollectionModel]? {
// 获取 JSON 文件路径
guard let path = Bundle.main.path(forResource: "ChargeItemsData", ofType: "json") else {
print("未找到 JSON 文件")
return nil
}
do {
// 读取文件内容
let data = try Data(contentsOf: URL(fileURLWithPath: path))
// 解析 JSON 数据
let decoder = JSONDecoder()
let items = try decoder.decode([ChargeViewCollectionModel].self, from: data)
return items
} catch {
print("解析 JSON 失败:\(error)")
return nil
}
}
struct ChargeViewCollectionModel:Codable {
var isFree:Bool
var CoverImage:String
var filePath:String
enum Category: String, Codable {
case isFree,CoverImage,filePath
}
}
//
// ChargeInfoBackView.swift
// PhoneManager
//
// Created by edy on 2025/3/27.
//
import UIKit
import SnapKit
class ChargeInfoBackView:UIView {
var backImage:String?
private var timer: Timer?
lazy var backImageView:UIImageView = {
let sview:UIImageView = UIImageView()
sview.contentMode = .scaleToFill
sview.clipsToBounds = true
sview.isUserInteractionEnabled = true
return sview
}()
let timeLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 72, weight: .bold)
label.textColor = .white
label.textAlignment = .center
label.sizeToFit()
return label
}()
let weekLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 20, weight: .regular)
label.textColor = .white
label.textAlignment = .center
label.sizeToFit()
return label
}()
let batteryLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 40, weight: .regular)
label.textColor = .white
label.textAlignment = .center
label.sizeToFit()
return label
}()
init(frame: CGRect,backImage:String) {
self.backImage = backImage
super.init(frame: frame)
setupUI()
setupTimeUpdates()
BatteryMonitorManager.shared.delegate = self
BatteryMonitorManager.shared.startMonitoring()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupUI() {
backImageView.image = UIImage(named: self.backImage ?? "")
self.addSubview(backImageView)
self.addSubview(timeLabel)
self.addSubview(weekLabel)
self.addSubview(batteryLabel)
}
func setupTimeUpdates() {
// 初始更新
updateTime()
startTimer()
// 监听系统时间变化通知
NotificationCenter.default.addObserver(
self,
selector: #selector(updateTime),
name: UIApplication.significantTimeChangeNotification,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(updateTime),
name: UIApplication.willEnterForegroundNotification,
object: nil
)
}
func startTimer() {
stopTimer()
timer = Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { [weak self] _ in
self?.updateTime()
}
}
func stopTimer() {
timer?.invalidate()
timer = nil
}
@objc func updateTime() {
let text = timeFormatter()
let attributedString = NSMutableAttributedString(string: text)
// 设置前部文本样式(除最后2个字符外)
let mainTextRange = NSRange(location: 0, length: max(text.count - 2, 0))
if mainTextRange.length > 0 {
attributedString.addAttribute(
.font,
value: UIFont.systemFont(ofSize: 72, weight: .regular),
range: mainTextRange
)
}
// 设置最后2个字符的样式
let lastTwoRange = NSRange(location: max(text.count - 2, 0), length: min(2, text.count))
if lastTwoRange.length > 0 {
attributedString.addAttribute(
.font,
value: UIFont.systemFont(ofSize: 28, weight: .regular),
range: lastTwoRange
)
}
// 应用到UILabel
DispatchQueue.main.async {
self.timeLabel.attributedText = attributedString
self.weekLabel.text = weekFormatter()
}
}
func changeBattery(level:Float) {
let text = String(format: "%.0f", (level * 100)) + "%"
let attributedString = NSMutableAttributedString(string: text)
// 设置前部文本样式(除最后2个字符外)
let mainTextRange = NSRange(location: 0, length: max(text.count - 1, 0))
if mainTextRange.length > 0 {
attributedString.addAttribute(
.font,
value: UIFont.systemFont(ofSize: 40, weight: .regular),
range: mainTextRange
)
}
// 设置最后2个字符的样式
let lastTwoRange = NSRange(location: max(text.count - 1, 0), length: min(1, text.count))
if lastTwoRange.length > 0 {
attributedString.addAttribute(
.font,
value: UIFont.systemFont(ofSize: 24, weight: .regular),
range: lastTwoRange
)
}
// 应用到UILabel
DispatchQueue.main.async {
self.batteryLabel.attributedText = attributedString
}
}
override func layoutSubviews() {
super.layoutSubviews()
backImageView.snp.makeConstraints { make in
make.centerX.width.height.equalToSuperview()
}
timeLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(statusBarHeight + 76)
}
weekLabel.sizeToFit()
weekLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(timeLabel.snp.bottom)
}
batteryLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.bottom.equalToSuperview().offset(-(safeHeight + 76))
}
}
deinit {
stopTimer()
BatteryMonitorManager.shared.stopMonitoring()
NotificationCenter.default.removeObserver(self)
Print("deinit")
}
}
extension ChargeInfoBackView:BatteryStatusDelegate {
func batteryStatusDidUpdate(level: Float, state: UIDevice.BatteryState) {
changeBattery(level: level)
}
}
//
// ChargeInfoSettingView.swift
// PhoneManager
//
// Created by edy on 2025/3/27.
//
import UIKit
class ChargeInfoSettingView:UIView {
lazy var settingBtn:UIButton = {
let sview:UIButton = UIButton()
sview.backgroundColor = UIColor.colorWithHex(hexStr: mColor)
sview.setTitle("Set Animation", for: .normal)
sview.setTitleColor(UIColor.white, for: .normal)
sview.titleLabel?.font = .systemFont(ofSize: 16, weight: .bold)
sview.width = width - 2 * marginLR
sview.height = 46
sview.centerX = width / 2
sview.y = marginLR
sview.layer.cornerRadius = sview.height / 2
sview.layer.masksToBounds = true
sview.addTarget(self, action: #selector(settingBtnClick), for: .touchUpInside)
return sview
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupUI() {
backgroundColor = .white
self.addSubview(settingBtn)
}
@objc func settingBtnClick() {
}
}
//
// ChargeView.swift
// PhoneManager
//
// Created by edy on 2025/3/26.
//
import UIKit
class ChargeView:UIView {
var callBack:callBack<Any> = {model in }
let footerID:String = "footerID"
lazy var models:[ChargeViewCollectionModel] = []
lazy var collectionView:UICollectionView = {
let cY:CGFloat = 0.RW()
let layout = WaterfallMutiSectionFlowLayout()
layout.delegate = self
let sview:UICollectionView = UICollectionView.init(frame: CGRect(x: marginLR, y: cY, width: width - 2 * marginLR, height: height - cY), collectionViewLayout: layout)
sview.dataSource = self
sview.delegate = self
sview.showsVerticalScrollIndicator = false
sview.register(ChargeViewCollectionCell.self, forCellWithReuseIdentifier: ChargeViewCollectionCell.identifiers)
sview.register(
ChargeHeaderView.self,
forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader,
withReuseIdentifier: ChargeHeaderView.identifiers
)
sview.register(
UICollectionReusableView.self,
forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter,
withReuseIdentifier: footerID
)
if #available(iOS 11.0, *) {
sview.contentInsetAdjustmentBehavior = .never
}
sview.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: safeHeight + 16, right: 0)
sview.backgroundColor = .clear
return sview
}()
override init(frame: CGRect) {
super.init(frame: frame)
models = loadChargeImtesSONFromBundle() ?? []
setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupUI() {
backgroundColor = .white
self.addSubview(collectionView)
}
}
extension ChargeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICollectionViewDelegate {
func heightForRowAtIndexPath(collectionView collection: UICollectionView, layout: WaterfallMutiSectionFlowLayout, indexPath: IndexPath, itemWidth: CGFloat) -> CGFloat {
return 297
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return models.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ChargeViewCollectionCell.identifiers, for: indexPath) as! ChargeViewCollectionCell
cell.model = models[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let model = models[indexPath.row]
self.callBack(model)
}
func collectionView(_ collectionView: UICollectionView,
viewForSupplementaryElementOfKind kind: String,
at indexPath: IndexPath) -> UICollectionReusableView {
if kind == UICollectionView.elementKindSectionHeader {
let header = collectionView.dequeueReusableSupplementaryView(
ofKind: kind,
withReuseIdentifier: ChargeHeaderView.identifiers,
for: indexPath
) as! ChargeHeaderView
header.titleLabel.text = "Charging Animation"
return header
}else {
let footer = collectionView.dequeueReusableSupplementaryView(
ofKind: kind,
withReuseIdentifier: footerID,
for: indexPath
)
return footer
}
}
func columnNumber(collectionView collection: UICollectionView, layout: WaterfallMutiSectionFlowLayout, section: Int) -> Int {
return 2
}
func lineSpacing(collectionView collection: UICollectionView, layout: WaterfallMutiSectionFlowLayout, section: Int) -> CGFloat {
return 11
}
func interitemSpacing(collectionView collection: UICollectionView, layout: WaterfallMutiSectionFlowLayout, section: Int) -> CGFloat {
return 11
}
func referenceSizeForHeader(collectionView collection: UICollectionView, layout: WaterfallMutiSectionFlowLayout, section: Int) -> CGSize {
return CGSize(width: collection.width, height: 56)
}
}
class ChargeHeaderView: UICollectionReusableView {
static let identifiers = "ChargeHeaderViewID"
let titleLabel: UILabel = {
let label = UILabel()
label.font = UIFont.boldSystemFont(ofSize: 20)
label.textColor = UIColor.colorWithHex(hexStr: black3Color)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupViews() {
backgroundColor = .clear
addSubview(titleLabel)
NSLayoutConstraint.activate([
titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0),
titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0),
titleLabel.topAnchor.constraint(equalTo: topAnchor),
titleLabel.bottomAnchor.constraint(equalTo: bottomAnchor)
])
}
}
//
// ChargeViewCollectionCell.swift
// PhoneManager
//
// Created by edy on 2025/3/26.
//
import UIKit
import SnapKit
class ChargeViewCollectionCell:UICollectionViewCell {
static let identifiers = "ChargeViewCollectionCellID"
lazy var backImageView:UIImageView = {
let sview:UIImageView = UIImageView()
sview.contentMode = .scaleAspectFill
sview.clipsToBounds = true
return sview
}()
lazy var isFreeBtn:UIButton = {
let sview:UIButton = UIButton()
sview.setImage(UIImage(named: "ic_collect_com"), for: .normal)
return sview
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
addSubview1()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupUI() {
backgroundColor = UIColor.random
layer.cornerRadius = 12
layer.masksToBounds = true
}
func addSubview1() {
self.contentView.addSubview(backImageView)
self.contentView.addSubview(isFreeBtn)
}
var model:ChargeViewCollectionModel! {
didSet {
DispatchQueue.main.async {[weak self] in
guard let self else {return}
self.backImageView.image = UIImage.init(named: model.CoverImage)
self.isFreeBtn.isHidden = model.isFree
}
}
}
override func layoutSubviews() {
super.layoutSubviews()
backImageView.snp.makeConstraints { make in
make.center.width.height.equalToSuperview()
}
isFreeBtn.snp.makeConstraints { make in
make.top.equalToSuperview().offset(12)
make.right.equalToSuperview().offset(-12)
make.width.height.equalTo(24)
}
}
}
//
// HomeInfoViewController.swift
// PhoneManager
//
// Created by edy on 2025/3/25.
//
import UIKit
class HomeInfoViewController:BaseViewController {
private var type:PhotsFileType?
lazy var seletedAllBtn:UIButton = {
let btn:UIButton = UIButton(frame: CGRect(x: 0, y: 0, width: 115, height: 32))
btn.addTarget(self, action: #selector(seletedAllBtnClick), for: .touchUpInside)
btn.backgroundColor = UIColor.colorWithHex(hexStr: "#F2F6FC")
btn.setImage(UIImage.init(named: "ic_check_similar"), for: .normal)
btn.setTitle("Select All", for: .normal)
btn.setImage(UIImage.init(named: "ic_close_similar"), for: .selected)
btn.setTitle("Deselect All", for: .selected)
btn.setTitleColor(UIColor.colorWithHex(hexStr: mColor), for: .normal)
btn.setTitleColor(UIColor.colorWithHex(hexStr: black3Color), for: .selected)
btn.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .bold)
btn.addTarget(self, action: #selector(seletedAllBtnClick), for: .touchUpInside)
btn.layer.cornerRadius = btn.height / 2
btn.layer.masksToBounds = true
btn.changBtnWithStytl(btnStyle: .defalut, margin: 8)
return btn
}()
lazy var tablewView:HomeInfoView = {
let cY:CGFloat = titleView.y + titleView.height
let sview:HomeInfoView = HomeInfoView(frame: CGRect(x: 0, y: cY, width: view.width, height: view.height - cY), ids: ids,type: self.type)
sview.callBack = {[weak self] isSeleted in
guard let self else {return}
if let cS = isSeleted as? Bool {
DispatchQueue.main.async {[weak self] in
guard let self else {return}
self.seletedAllBtn.isSelected = cS
self.seletedAllBtn.width = cS ? 131 : 115
seletedAllBtn.x = titleView.width - marginLR - seletedAllBtn.width
}
}
}
sview.deleteCallBack = {array in
if let cA = array as? [String] {
PhotoAndVideoMananger.deleteAssets(localIdentifiers: cA) {[weak self] in
guard let self else {return}
self.tablewView.deleteModel()
}
}
}
return sview
}()
var ids: [[String]]?
init(ids: [[String]],type:PhotsFileType?) {
self.ids = ids
self.type = type
super.init(nibName: nil, bundle: nil)
}
// 由于继承自 UIViewController,必须实现这个必需的构造器
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override func viewDidLoad() {
super.viewDidLoad()
titleView.model.title = ""
}
override func addViews() {
super.addViews()
seletedAllBtn.x = titleView.width - marginLR - seletedAllBtn.width
seletedAllBtn.centerY = navCenterY
titleView.addSubview(seletedAllBtn)
view.addSubview(tablewView)
}
@objc func seletedAllBtnClick() {
DispatchQueue.main.async {[weak self] in
guard let self else {return}
seletedAllBtn.isSelected = !seletedAllBtn.isSelected
self.seletedAllBtn.width = seletedAllBtn.isSelected ? 131 : 115
seletedAllBtn.x = titleView.width - marginLR - seletedAllBtn.width
tablewView.changeALlValue(isSeleted: seletedAllBtn.isSelected)
}
}
}
...@@ -8,11 +8,11 @@ ...@@ -8,11 +8,11 @@
import UIKit import UIKit
class HomeViewController:UIViewController { class HomeViewController:BaseViewController {
private var isShowPay:Bool = false private var isShowPay:Bool = false
private var homeView:HomeView? var homeView:HomeView?
override func viewDidLoad() { override func viewDidLoad() {
...@@ -22,24 +22,109 @@ class HomeViewController:UIViewController { ...@@ -22,24 +22,109 @@ class HomeViewController:UIViewController {
homeView = HomeView(frame: view.bounds) homeView = HomeView(frame: view.bounds)
homeView?.titleCallBack = {[weak self] array in
DispatchQueue.main.async {[weak self] in
guard let self else {return}
let vc:HomeInfoViewController = HomeInfoViewController(ids: array as! [[String]], type: .similar)
self.navigationController?.pushViewController(vc, animated: true)
}
}
homeView?.indexCallBack = {[weak self] index in
guard let self else {return}
if let cIndex = index as? Int {
switch cIndex {
case 0 :
DispatchQueue.main.async {[weak self] in
guard let self else {return}
let vc:ChargeViewController = ChargeViewController()
self.navigationController?.pushViewController(vc, animated: true)
}
default:
break
}
}
}
view.addSubview(homeView!) view.addSubview(homeView!)
}
override func addViews() {
}
func setupData() {
PhotoDataManager.manager.loadFromFileSystem(resultModel: {[weak self] model in
self?.homeView?.model = model
})
PhotoAndVideoMananger.mananger.fetchAllFile {[weak self] index, FileSize in
guard let self else {return}
self.homeView?.model?.allFileNumber = index
self.homeView?.model?.allFileSize = FileSize
self.homeView?.setTitle()
} completion: {[weak self] fileSize,index in
guard let self else {return}
self.homeView?.model?.allFileNumber = index
self.homeView?.model?.allFileSize = fileSize
self.homeView?.setTitle()
}
} }
override func viewDidAppear(_ animated: Bool) { override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated) super.viewDidAppear(animated)
self.barHidden = false
if !isShowPay { if !isShowPay {
isShowPay = true setupData()
let vc:HomePayViewController = HomePayViewController()
let nav:BaseNavViewController = BaseNavViewController(rootViewController: vc)
nav.modalPresentationStyle = .fullScreen isShowPay = true
self.navigationController?.present(nav, animated: true) if BatteryMonitorManager.shared.getBatteryIsCharging() {
let vc:ChargeInfoViewController = ChargeInfoViewController(model:loadChargeImtesSONFromBundle()?.first, type: ChargeInfoViewController.ChargeInfoType.charge)
self.navigationController?.pushViewController(vc, animated: false)
}else {
let vc:HomePayViewController = HomePayViewController()
let nav:BaseNavViewController = BaseNavViewController(rootViewController: vc)
nav.modalPresentationStyle = .fullScreen
self.navigationController?.present(nav, animated: true)
}
} }
} }
} }
This diff is collapsed.
...@@ -74,7 +74,21 @@ class HomeNavView:UIView { ...@@ -74,7 +74,21 @@ class HomeNavView:UIView {
self.addSubview(tipLabel) self.addSubview(tipLabel)
// 设置文本内容 // 设置文本内容
let text = "202 files · 1.15 GB of storage to clean up"
}
@objc private func settingBtnClick() {
}
func setFileAndCount(count:Int,fileSize:Double) {
let countString = "\(count)"
let fileSizeString = formatFileSize(fileSize)
let text = countString + " files · " + fileSizeString + " of storage to clean up"
let attributedText = NSMutableAttributedString(string: text) let attributedText = NSMutableAttributedString(string: text)
// 设置整体文本样式 // 设置整体文本样式
...@@ -85,17 +99,19 @@ class HomeNavView:UIView { ...@@ -85,17 +99,19 @@ class HomeNavView:UIView {
attributedText.addAttributes(fullTextAttributes, range: NSRange(location: 0, length: text.count)) attributedText.addAttributes(fullTextAttributes, range: NSRange(location: 0, length: text.count))
// 设置 "202" 为蓝色 // 设置 "202" 为蓝色
if let range1 = text.range(of: "202") { if let range1 = text.range(of: countString) {
let nsRange1 = NSRange(range1, in: text) let nsRange1 = NSRange(range1, in: text)
attributedText.addAttributes([ attributedText.addAttributes([
.foregroundColor: UIColor.colorWithHex(hexStr: mColor) .foregroundColor: UIColor.colorWithHex(hexStr: mColor),
.font:UIFont.systemFont(ofSize: 14, weight: .bold)
], range: nsRange1) ], range: nsRange1)
} }
// 设置 "1.15 GB" 为蓝色 // 设置 "1.15 GB" 为蓝色
if let range2 = text.range(of: "1.15 GB") { if let range2 = text.range(of: fileSizeString) {
let nsRange2 = NSRange(range2, in: text) let nsRange2 = NSRange(range2, in: text)
attributedText.addAttributes([ attributedText.addAttributes([
.font:UIFont.systemFont(ofSize: 14, weight: .bold),
.foregroundColor: UIColor.colorWithHex(hexStr: mColor) .foregroundColor: UIColor.colorWithHex(hexStr: mColor)
], range: nsRange2) ], range: nsRange2)
} }
...@@ -109,11 +125,5 @@ class HomeNavView:UIView { ...@@ -109,11 +125,5 @@ class HomeNavView:UIView {
make.centerX.equalToSuperview() make.centerX.equalToSuperview()
make.width.equalToSuperview().offset(-2 * marginLR) make.width.equalToSuperview().offset(-2 * marginLR)
} }
}
@objc private func settingBtnClick() {
} }
} }
...@@ -11,6 +11,8 @@ class HomeTabbarView:UIView { ...@@ -11,6 +11,8 @@ class HomeTabbarView:UIView {
private var tabbarItems:[HomeTabbarItem] = [] private var tabbarItems:[HomeTabbarItem] = []
var indexCallBack:callBack<Any> = {index in }
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
...@@ -48,7 +50,7 @@ class HomeTabbarView:UIView { ...@@ -48,7 +50,7 @@ class HomeTabbarView:UIView {
btn.addTarget(self, action: #selector(tabbarClick(_:)), for: .touchUpInside) btn.addTarget(self, action: #selector(tabbarClick(_:)), for: .touchUpInside)
btn.width = cW btn.width = cW
btn.height = cH btn.height = cH
btn.y = 12 btn.y = safeHeight == 0 ? 6 : 12
btn.x = 8 + Double(index) * cW btn.x = 8 + Double(index) * cW
btn.changBtnWithStytl(btnStyle: .imageTop, margin: 5) btn.changBtnWithStytl(btnStyle: .imageTop, margin: 5)
...@@ -64,7 +66,13 @@ class HomeTabbarView:UIView { ...@@ -64,7 +66,13 @@ class HomeTabbarView:UIView {
@objc func tabbarClick(_ btn:UIButton) { @objc func tabbarClick(_ btn:UIButton) {
let btnText = btn.titleLabel?.text
for (index, item) in tabbarItems.enumerated() {
if item.text == btnText {
indexCallBack(index)
}
}
} }
......
...@@ -16,9 +16,24 @@ class HomeView:UIView { ...@@ -16,9 +16,24 @@ class HomeView:UIView {
private var bottomView:UIView? private var bottomView:UIView?
private var titleModels:[HomePhotosModel] = [] var titleCallBack:callBack<Any> = {array in}
private var contentModels:[String] = ["123","1234","12323","12"] var indexCallBack:callBack<Any> = {index in }
var model:PhotosManagerModel? {
didSet {
guard model != nil else {return}
DispatchQueue.main.async {[weak self] in
guard let self else {return}
self.collectionView.reloadData()
}
}
}
lazy var collectionView:UICollectionView = { lazy var collectionView:UICollectionView = {
...@@ -32,6 +47,11 @@ class HomeView:UIView { ...@@ -32,6 +47,11 @@ class HomeView:UIView {
sview.delegate = self sview.delegate = self
sview.showsVerticalScrollIndicator = false sview.showsVerticalScrollIndicator = false
sview.register(HomeTitleCollectionCell.self, forCellWithReuseIdentifier: HomeTitleCollectionCell.identifiers) sview.register(HomeTitleCollectionCell.self, forCellWithReuseIdentifier: HomeTitleCollectionCell.identifiers)
sview.register(HomeOtherCollectionCell.self, forCellWithReuseIdentifier: HomeOtherCollectionCell.identifier)
if #available(iOS 11.0, *) {
sview.contentInsetAdjustmentBehavior = .never
}
sview.backgroundColor = .clear sview.backgroundColor = .clear
return sview return sview
...@@ -50,12 +70,76 @@ class HomeView:UIView { ...@@ -50,12 +70,76 @@ class HomeView:UIView {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
func reload(type:PhotsFileType) {
var indexPath:IndexPath!
if type == .duplicates {
indexPath = IndexPath(row: 0, section: 0)
}else if type == .similar{
indexPath = IndexPath(row: 1, section: 0)
}else if type == .videos{
indexPath = IndexPath(row: 0, section: 1)
}
DispatchQueue.main.async {[weak self] in
guard let self else {return}
self.collectionView.reloadData()
}
}
func setTitle() {
DispatchQueue.main.async {[weak self] in
guard let self else {return}
self.homeNavView?.setFileAndCount(count: model?.allFileNumber ?? 0, fileSize: model?.allFileSize ?? 0)
}
}
func refreshData(model:PhotosManagerModel) {
if self.model == nil {
DispatchQueue.main.async {[weak self] in
guard let self else {return}
self.homeNavView?.setFileAndCount(count: model.allFileNumber, fileSize: model.allFileSize)
self.collectionView.reloadData()
}
}else {
DispatchQueue.main.async {[weak self] in
guard let self else {return}
self.homeNavView?.setFileAndCount(count: model.allFileNumber, fileSize: model.allFileSize)
self.collectionView.reloadData()
}
}
self.model = model
}
func setData() { func setData() {
let model:HomePhotosModel = HomePhotosModel(folderName: "Duplicates", filePaths: [], firstItemPath: "dfdf", allFileSize: 123213132)
titleModels.append(model)
titleModels.append(model)
} }
private func setupUI() { private func setupUI() {
...@@ -72,8 +156,16 @@ class HomeView:UIView { ...@@ -72,8 +156,16 @@ class HomeView:UIView {
make.height.equalTo(safeHeight + 66) make.height.equalTo(safeHeight + 66)
}) })
homeTabbarView?.indexCallBack = {[weak self] index in
guard let self else {return}
self.indexCallBack(index)
}
homeNavView = HomeNavView(frame: CGRect(x: 0, y: 0, width: width, height: statusBarHeight + 96)) homeNavView = HomeNavView(frame: CGRect(x: 0, y: 0, width: width, height: statusBarHeight + 96))
self.addSubview(homeNavView!) self.addSubview(homeNavView!)
homeNavView?.snp.makeConstraints({ make in homeNavView?.snp.makeConstraints({ make in
...@@ -82,6 +174,8 @@ class HomeView:UIView { ...@@ -82,6 +174,8 @@ class HomeView:UIView {
make.height.equalTo(statusBarHeight + 96) make.height.equalTo(statusBarHeight + 96)
}) })
Print("statusBarHeight----\(statusBarHeight)")
bottomView = UIView(frame: CGRect(x: 0, y: 0, width: width, height: safeHeight + 10)) bottomView = UIView(frame: CGRect(x: 0, y: 0, width: width, height: safeHeight + 10))
bottomView?.backgroundColor = .white bottomView?.backgroundColor = .white
self.addSubview(bottomView!) self.addSubview(bottomView!)
...@@ -92,14 +186,16 @@ class HomeView:UIView { ...@@ -92,14 +186,16 @@ class HomeView:UIView {
make.height.equalTo(safeHeight + 10) make.height.equalTo(safeHeight + 10)
}) })
collectionView.contentInset = UIEdgeInsets(top: homeTabbarView!.height, left: 0, bottom: homeTabbarView!.height - safeHeight + 16, right: 0)
self.insertSubview(collectionView, at: 0) self.insertSubview(collectionView, at: 0)
collectionView.snp.makeConstraints { make in collectionView.snp.makeConstraints { make in
make.center.height.equalToSuperview() make.center.height.equalToSuperview()
make.width.equalToSuperview().offset(-2 * marginLR) make.width.equalToSuperview().offset(-2 * marginLR)
} }
collectionView.contentInset = UIEdgeInsets(top: homeTabbarView!.height + 49, left: 0, bottom: homeTabbarView!.height + 16, right: 0)
} }
func etCell(indexPath: IndexPath) -> UICollectionViewCell { func etCell(indexPath: IndexPath) -> UICollectionViewCell {
...@@ -120,10 +216,10 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol ...@@ -120,10 +216,10 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol
switch section { switch section {
case 0: case 0:
return titleModels.count return model?.titleModelArray.count ?? 0
case 1: case 1:
return contentModels.count return model?.otherModelArray.count ?? 0
default: default:
return 0 return 0
...@@ -132,28 +228,36 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol ...@@ -132,28 +228,36 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: HomeTitleCollectionCell.identifiers, for: indexPath) as! HomeTitleCollectionCell
let section = indexPath.section let section = indexPath.section
switch section { switch section {
case 0: case 0:
cell.model = titleModels[indexPath.row] let cell = collectionView.dequeueReusableCell(withReuseIdentifier: HomeTitleCollectionCell.identifiers, for: indexPath) as! HomeTitleCollectionCell
cell.model = model?.titleModelArray[indexPath.row]
return cell
case 1:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: HomeOtherCollectionCell.identifier, for: indexPath) as! HomeOtherCollectionCell
cell.model = model?.otherModelArray[indexPath.row]
return cell
default: default:
break return UICollectionViewCell()
} }
return cell
} }
func heightForRowAtIndexPath(collectionView collection: UICollectionView, layout: WaterfallMutiSectionFlowLayout, indexPath: IndexPath, itemWidth: CGFloat) -> CGFloat { func heightForRowAtIndexPath(collectionView collection: UICollectionView, layout: WaterfallMutiSectionFlowLayout, indexPath: IndexPath, itemWidth: CGFloat) -> CGFloat {
if indexPath.section == 0 { if indexPath.section == 0 {
return 190 let model = model?.titleModelArray[indexPath.row]
return (model?.assets.first?.count ?? 0) > 2 ? ((collection.width - marginLR - 20) / 2.5) + 64 : ((collection.width - 2 * marginLR - 10) / 2) + 64
}else { }else {
return indexPath.row % 2 == 1 ? 197 : 217 let model = model?.otherModelArray[indexPath.row]
return itemWidth + 12 + UILabel.getSizeWith(font: UIFont.systemFont(ofSize: 16, weight: .bold),lineSpacing: 5, width: itemWidth - 32, numberOfLines: 0, content: model?.folderName ?? "").height
} }
} }
...@@ -197,4 +301,14 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol ...@@ -197,4 +301,14 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol
return 0 return 0
} }
} }
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if (indexPath.section == 0) {
let smodel = model?.titleModelArray[indexPath.row]
titleCallBack(smodel?.assets ?? [])
}
}
} }
...@@ -6,11 +6,151 @@ ...@@ -6,11 +6,151 @@
// //
import Foundation import Foundation
import Photos
struct HomePhotosModel { class PhotoDataManager {
static let manager:PhotoDataManager = PhotoDataManager()
// 文件路径
private func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return paths[0]
}
// 保存到文件
func saveToFileSystem(model: PhotosManagerModel, filename: String = "photosManagerData.json") {
let url = getDocumentsDirectory().appendingPathComponent(filename)
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
do {
let data = try encoder.encode(model)
try data.write(to: url)
print("数据保存成功: \(url.path)")
} catch {
print("保存失败: \(error.localizedDescription)")
}
}
// 从文件读取
func loadFromFileSystem(filename: String = "photosManagerData.json",resultModel:@escaping (_ model:PhotosManagerModel) -> () = {mdoel in}) {
let url = getDocumentsDirectory().appendingPathComponent(filename)
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let model = try decoder.decode(PhotosManagerModel.self, from: data)
resultModel(model)
} catch {
loadDataFromPhotos { model in
resultModel(model)
}
}
}
private func loadDataFromPhotos(resultModel:@escaping (_ model:PhotosManagerModel) -> () = {mdoel in}) {
let model1:HomePhotosModel = HomePhotosModel(folderName: PhotsFileType.duplicates.rawValue, allFileSize: 0, assets: [])
let model2:HomePhotosModel = HomePhotosModel(folderName: PhotsFileType.similar.rawValue, allFileSize: 0, assets: [])
let model3:HomePhotosModel = HomePhotosModel(folderName: PhotsFileType.videos.rawValue, allFileSize: 0, assets: [])
let model4:HomePhotosModel = HomePhotosModel(folderName: PhotsFileType.similarScreenshots.rawValue, allFileSize: 0, assets: [])
let model5:HomePhotosModel = HomePhotosModel(folderName: PhotsFileType.screenshots.rawValue, allFileSize: 0, assets: [])
let model6:HomePhotosModel = HomePhotosModel(folderName: PhotsFileType.SimilarVideos.rawValue, allFileSize: 0, assets: [])
let model7:HomePhotosModel = HomePhotosModel(folderName: PhotsFileType.Other.rawValue, allFileSize: 0, assets: [])
let allModel:PhotosManagerModel = PhotosManagerModel(allFileNumber: 0, allFileSize: 0, titleModelArray: [model1,model2], otherModelArray: [model3,model4,model5,model6,model7])
resultModel(allModel)
PhotoAndVideoMananger.mananger.fetXSOther { array in
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
PhotoSimilarityFinder.processSimilarPhotoGroups(simalr:1,assetGroups: array) { array,fileSize in
model1.assets = array
model1.allFileSize = fileSize
resultModel(allModel)
dispatchGroup.leave()
}
dispatchGroup.enter()
PhotoSimilarityFinder.processSimilarPhotoGroups(assetGroups: array) {array,fileSize in
model2.assets = array
model2.allFileSize = fileSize
resultModel(allModel)
dispatchGroup.leave()
}
dispatchGroup.enter()
PhotoAndVideoMananger.mananger.fetXSVideo { array in
PhotoSimilarityFinder.processSimilarVideoGroups(videoGroups: array) {ids in
model3.assets = ids
resultModel(allModel)
dispatchGroup.leave()
}
}
dispatchGroup.notify(queue: .main) {
print("所有异步操作都已完成")
if model1.assets.count != 0 || model2.assets.count != 0 || model3.assets.count != 0 {
PhotoDataManager.manager.saveToFileSystem(model: allModel)
resultModel(allModel)
}
}
}
}
}
class PhotosManagerModel:Codable {
var allFileNumber:Int
var allFileSize:Double
var titleModelArray:[HomePhotosModel] = []
var otherModelArray:[HomePhotosModel] = []
init(allFileNumber: Int, allFileSize: Double, titleModelArray: [HomePhotosModel], otherModelArray: [HomePhotosModel]) {
self.allFileNumber = allFileNumber
self.allFileSize = allFileSize
self.titleModelArray = titleModelArray
self.otherModelArray = otherModelArray
}
}
class HomePhotosModel:Codable {
var folderName:String var folderName:String
var filePaths:[String]
var firstItemPath:String
var allFileSize:Double var allFileSize:Double
var assets:[[String]]
init(folderName: String, allFileSize: Double, assets: [[String]]) {
self.folderName = folderName
self.allFileSize = allFileSize
self.assets = assets
}
} }
//
// ImageCollectionModel.swift
// PhoneManager
//
// Created by edy on 2025/3/24.
//
import Foundation
import Photos
struct ImageCollectionModel {
var asset:String
}
//
// ImageSeletedCollectionItem.swift
// PhoneManager
//
// Created by edy on 2025/3/26.
//
import Foundation
import UIKit
class ImageSeletedCollectionItem {
var id:String = ""
var image:UIImage?
var isSeleted:Bool?
}
class HomeInfoTableItem {
var type:PhotsFileType?
var smodels:[ImageSeletedCollectionItem]?
}
//
// HomeInfoTableViewCell.swift
// PhoneManager
//
// Created by edy on 2025/3/25.
//
import UIKit
import SnapKit
class HomeInfoTableViewCell:UITableViewCell {
static let identifier = "HomeInfoTableViewCellID"
private var backView:UIView?
private var collectionView: UICollectionView?
private var numberLabel:UILabel?
private var seletedAllBtn:UIButton?
var callBack:callBack<Any> = {text in}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupUI()
addSubview1()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupUI() {
self.selectionStyle = .none
backView = UIView()
backView?.backgroundColor = UIColor.colorWithHex(hexStr: "#F2F6FC")
backView?.layer.cornerRadius = 12
backView?.layer.masksToBounds = true
backView?.x = marginLR
backView?.y = marginLR
backView?.isUserInteractionEnabled = true
numberLabel = UILabel()
numberLabel?.textColor = UIColor.colorWithHex(hexStr: black3Color)
numberLabel?.font = UIFont.systemFont(ofSize: 16, weight: .bold)
seletedAllBtn = UIButton()
seletedAllBtn?.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .bold)
seletedAllBtn?.setTitle("Select All", for: .normal)
seletedAllBtn?.setTitle("Deselect All", for: .selected)
seletedAllBtn?.setTitleColor(UIColor.colorWithHex(hexStr: mColor), for: .normal)
seletedAllBtn?.sizeToFit()
seletedAllBtn?.addTarget(self, action: #selector(seletedAllBtnClick), for: .touchUpInside)
let flowlayout:UICollectionViewFlowLayout = UICollectionViewFlowLayout()
flowlayout.scrollDirection = .horizontal
flowlayout.minimumLineSpacing = 10
collectionView = UICollectionView.init(frame: CGRectMake(0, 0 , ScreenW , ScreenH), collectionViewLayout: flowlayout)
collectionView?.backgroundColor = .clear
collectionView?.showsHorizontalScrollIndicator = false
collectionView?.showsVerticalScrollIndicator = false
collectionView?.register(ImageSeletedCollectionCell.self, forCellWithReuseIdentifier: ImageSeletedCollectionCell.identifiers)
collectionView?.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: marginLR)
collectionView?.dataSource = self
collectionView?.delegate = self
}
func addSubview1() {
contentView.addSubview(backView!)
contentView.addSubview(numberLabel!)
contentView.addSubview(seletedAllBtn!)
contentView.addSubview(collectionView!)
}
var model:HomeInfoTableItem? {
didSet {
guard let model else {return}
let cH = ((model.smodels?.count ?? 0) > 2 ? 190 : 214) + 8
backView?.height = CGFloat(cH - 8)
backView?.width = ScreenW - 16 * 2
backView?.centerY = CGFloat(cH / 2)
backView?.centerX = ScreenW / 2
numberLabel?.text = "\(model.smodels?.count ?? 0) " + "Duplicates"
numberLabel?.sizeToFit()
numberLabel?.x = (backView?.x ?? 0) + marginLR
numberLabel?.y = (backView?.y ?? 0) + marginLR
seletedAllBtn?.centerY = numberLabel?.centerY ?? 0
seletedAllBtn?.x = (backView?.x ?? 0) + (backView?.width ?? 0) - 12 - (seletedAllBtn?.width ?? 0)
checkSeletedAll()
collectionView?.height = CGFloat(cH - 64)
collectionView?.width = (backView?.width ?? 0) - marginLR
collectionView?.x = (backView?.x ?? 0) + marginLR
collectionView?.y = (backView?.y ?? 0) + (backView?.height ?? 0) - (collectionView?.height ?? 0) - 16
self.collectionView?.reloadData()
}
}
@objc func seletedAllBtnClick() {
DispatchQueue.main.async {[weak self] in
guard let self else {return}
seletedAllBtn?.isSelected = !(seletedAllBtn?.isSelected ?? true)
seletedAllBtn?.sizeToFit()
seletedAllBtn?.centerY = numberLabel?.centerY ?? 0
seletedAllBtn?.x = (backView?.x ?? 0) + (backView?.width ?? 0) - 12 - (seletedAllBtn?.width ?? 0)
}
for (index,smodel) in (model?.smodels ?? []).enumerated() {
if index == 0 {
smodel.isSeleted = false
}else {
smodel.isSeleted = !(seletedAllBtn?.isSelected ?? false)
}
}
collectionView?.reloadData()
callBack("changgeSeleted")
}
func checkSeletedAll() {
var isSeletedAll:Bool = false
for smodel in model?.smodels ?? [] {
if (smodel.isSeleted ?? false) {
isSeletedAll = true
}
}
seletedAllBtn?.isSelected = isSeletedAll
callBack("changgeSeleted")
DispatchQueue.main.async {[weak self] in
guard let self else {return}
seletedAllBtn?.sizeToFit()
seletedAllBtn?.centerY = numberLabel?.centerY ?? 0
seletedAllBtn?.x = (backView?.x ?? 0) + (backView?.width ?? 0) - 12 - (seletedAllBtn?.width ?? 0)
}
}
override func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
extension HomeInfoTableViewCell:UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return model?.smodels?.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ImageSeletedCollectionCell.identifiers, for: indexPath) as! ImageSeletedCollectionCell
cell.model = model?.smodels?[indexPath.row]
cell.callBack = {[weak self] _ in
guard let self else {return}
self.checkSeletedAll()
self.callBack("changgeSeleted")
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// 计算 cell 宽度
/*return CGSize(width:collectionView.height, height: collectionView.height) */ // 宽高相等,形成网格
return CGSize(width:collectionView.height, height: collectionView.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 10 // 设置列间距
}
}
struct HomeInfoTableViewModel {
var ids:[String]?
var height:CGFloat?
}
import UIKit
class HomeOtherCollectionCell: UICollectionViewCell {
// MARK: - Properties
static let identifier = "HomeOtherCollectionCellID"
private let imageView: UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFill
iv.clipsToBounds = true
iv.layer.cornerRadius = 8
iv.backgroundColor = .clear
return iv
}()
private let countLabel: UILabel = {
let label = UILabel()
label.textColor = .white
label.font = .systemFont(ofSize: 16, weight: .medium)
label.textAlignment = .center
return label
}()
private let sizeLabel: UILabel = {
let label = UILabel()
label.textColor = .systemGray
label.font = .systemFont(ofSize: 12)
label.textAlignment = .left
return label
}()
private let titleLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 16, weight: .bold)
label.textColor = UIColor.colorWithHex(hexStr: black3Color)
label.textAlignment = .left
label.numberOfLines = 0
return label
}()
// MARK: - Lifecycle
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Setup
private func setupUI() {
backgroundColor = UIColor.colorWithHex(hexStr: "#F2F6FC")
contentView.addSubview(imageView)
contentView.addSubview(countLabel)
contentView.addSubview(sizeLabel)
contentView.addSubview(titleLabel)
}
var model:HomePhotosModel? {
didSet {
guard let model else {return}
titleLabel.text = model.folderName
guard let asset = model.assets.first?.first else {return}
let image = PhotoAndVideoMananger.mananger.getImageFromAssetID(id: asset)
DispatchQueue.main.async {[weak self] in
guard let self else {return}
imageView.image = image
}
}
}
// MARK: - Configuration
func configure(with image: UIImage?, count: Int, size: String) {
imageView.image = image
countLabel.text = "\(count) Photos"
sizeLabel.text = size
}
override func prepareForReuse() {
super.prepareForReuse()
imageView.image = nil
countLabel.text = nil
sizeLabel.text = nil
}
override func layoutSubviews() {
self.layer.cornerRadius = 12
self.layer.masksToBounds = true
imageView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.bottom.equalToSuperview().offset(-16)
make.width.equalToSuperview().offset(-32)
make.height.equalTo(self.width - 32)
}
titleLabel.snp.makeConstraints { make in
make.left.top.equalToSuperview().offset(16)
make.width.equalToSuperview().offset(-32)
}
titleLabel.sizeToFit()
}
}
...@@ -14,8 +14,14 @@ class HomeTitleCollectionCell:UICollectionViewCell { ...@@ -14,8 +14,14 @@ class HomeTitleCollectionCell:UICollectionViewCell {
private var titleLabel: UILabel? private var titleLabel: UILabel?
private var fileLabel:UILabel?
private var collectionView: UICollectionView? private var collectionView: UICollectionView?
private var nextImage:UIImageView?
private var assetsModels:[ImageCollectionModel] = []
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
...@@ -38,19 +44,75 @@ class HomeTitleCollectionCell:UICollectionViewCell { ...@@ -38,19 +44,75 @@ class HomeTitleCollectionCell:UICollectionViewCell {
titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .bold) titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .bold)
titleLabel?.textColor = UIColor.colorWithHex(hexStr: black3Color) titleLabel?.textColor = UIColor.colorWithHex(hexStr: black3Color)
fileLabel = UILabel()
fileLabel?.font = UIFont.systemFont(ofSize: 14, weight: .bold)
fileLabel?.textColor = UIColor.colorWithHex(hexStr: mColor)
let flowlayout:UICollectionViewFlowLayout = UICollectionViewFlowLayout()
flowlayout.scrollDirection = .horizontal
flowlayout.minimumLineSpacing = 10
collectionView = UICollectionView.init(frame: CGRectMake(0, 0 , ScreenW , ScreenH), collectionViewLayout: flowlayout)
collectionView?.backgroundColor = .clear
collectionView?.decelerationRate = UIScrollView.DecelerationRate.fast;
collectionView?.showsHorizontalScrollIndicator = false
collectionView?.showsVerticalScrollIndicator = false
collectionView?.register(ImageCollectionCell.self, forCellWithReuseIdentifier: ImageCollectionCell.identifiers)
collectionView?.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: marginLR)
collectionView?.dataSource = self
collectionView?.delegate = self
nextImage = UIImageView(image: UIImage(named: "icon_left_setting"))
} }
func addSubview1() { func addSubview1() {
addSubview(titleLabel!) addSubview(titleLabel!)
addSubview(fileLabel!)
addSubview(collectionView!)
addSubview(nextImage!)
} }
var model:HomePhotosModel! { var model:HomePhotosModel? {
didSet { didSet {
guard let model else {return}
titleLabel?.text = model.folderName titleLabel?.text = model.folderName
titleLabel?.sizeToFit() titleLabel?.sizeToFit()
var count = 0
for array in model.assets {
count += array.count
}
fileLabel?.text = "\(count)" + " Photos " + (model.allFileSize > 0 ? "(\(formatFileSize(model.allFileSize)))" : "(Calculating...)")
fileLabel?.sizeToFit()
assetsModels = []
for asset in model.assets.first ?? [] {
let smodel = ImageCollectionModel(asset: asset)
assetsModels.append(smodel)
}
Print(assetsModels)
Print(model.assets)
DispatchQueue.main.async {[weak self] in
guard let self else {return}
self.collectionView?.reloadData()
}
} }
} }
...@@ -62,6 +124,60 @@ class HomeTitleCollectionCell:UICollectionViewCell { ...@@ -62,6 +124,60 @@ class HomeTitleCollectionCell:UICollectionViewCell {
make.top.left.equalTo(16) make.top.left.equalTo(16)
}) })
titleLabel?.sizeToFit()
fileLabel?.snp.makeConstraints({ make in
make.top.centerY.equalTo(titleLabel!)
make.right.equalToSuperview().offset(-34)
})
fileLabel?.sizeToFit()
collectionView?.snp.makeConstraints({ make in
make.left.equalToSuperview().offset(16)
make.bottom.equalToSuperview().offset(-16)
make.width.equalToSuperview().offset((model?.assets.count ?? 0) > 2 ? -16 : -32)
make.height.equalToSuperview().offset(-64)
})
nextImage?.snp.makeConstraints({ make in
make.centerY.equalTo(fileLabel!)
make.right.equalToSuperview().offset(-12)
make.width.height.equalTo(20)
})
}
}
extension HomeTitleCollectionCell:UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return assetsModels.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ImageCollectionCell.identifiers, for: indexPath) as! ImageCollectionCell
cell.model = assetsModels[indexPath.row]
return cell
} }
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// 计算 cell 宽度
return CGSize(width:collectionView.height - 2.5, height: collectionView.height - 2.5) // 宽高相等,形成网格
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 10 // 设置列间距
}
} }
//
// ImageCollectionCell.swift
// PhoneManager
//
// Created by edy on 2025/3/24.
//
import UIKit
import SnapKit
class ImageCollectionCell:UICollectionViewCell {
static let identifiers = "ImageCollectionCellID"
private var backImageView: UIImageView?
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
addViews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var model:ImageCollectionModel! {
didSet {
DispatchQueue.global().async {[weak self] in
guard let self else {return}
let image = PhotoAndVideoMananger.mananger.getImageFromAssetID(id: model.asset)
DispatchQueue.main.async {[weak self] in
guard let self else {return}
backImageView?.image = image
}
}
}
}
func setupUI() {
backImageView = UIImageView()
backImageView?.isUserInteractionEnabled = true
backImageView?.contentMode = .scaleAspectFill
backImageView?.clipsToBounds = true
backImageView?.layer.masksToBounds = true
self.backgroundColor = .clear
}
func addViews() {
self.addSubview(backImageView!)
}
override func layoutSubviews() {
super.layoutSubviews()
backImageView?.snp.makeConstraints({ make in
make.top.left.width.height.equalToSuperview()
})
backImageView?.layer.cornerRadius = 12
}
}
//
// ImageSeletedCollectionCell.swift
// PhoneManager
//
// Created by edy on 2025/3/26.
//
import UIKit
import SnapKit
class ImageSeletedCollectionCell:UICollectionViewCell {
static let identifiers = "ImageSeletedCollectionCellID"
private var backImageView: UIImageView?
private var seletedBtn:UIButton?
var callBack:callBack<Any> = {text in}
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
addViews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var model:ImageSeletedCollectionItem! {
didSet {
DispatchQueue.main.async {[weak self] in
guard let self else {return}
seletedBtn?.isSelected = model.isSeleted ?? false
}
if let image = model.image {
DispatchQueue.main.async {[weak self] in
guard let self else {return}
backImageView?.image = image
}
}else {
DispatchQueue.global().async {[weak self] in
guard let self else {return}
if let asset = PhotoAndVideoMananger.mananger.getPHAsssetwithID(ids: [model.id]) {
let image = PhotoAndVideoMananger.mananger.getImageFromAsset(asset: asset)
model.image = image
DispatchQueue.main.async {[weak self] in
guard let self else {return}
backImageView?.image = image
}
}
}
}
}
}
func setupUI() {
backImageView = UIImageView()
backImageView?.contentMode = .scaleAspectFill
backImageView?.clipsToBounds = true
backImageView?.layer.masksToBounds = true
seletedBtn = UIButton()
seletedBtn?.setImage(UIImage(named: "home_info_norl"), for: .normal)
seletedBtn?.setImage(UIImage(named: "home_info_seleted"), for: .selected)
seletedBtn?.addTarget(self, action: #selector(seletedBtnClick), for: .touchUpInside)
self.backgroundColor = .clear
}
func addViews() {
self.addSubview(backImageView!)
self.addSubview(seletedBtn!)
}
@objc func seletedBtnClick() {
model.isSeleted = !(seletedBtn?.isSelected ?? true)
seletedBtn?.isSelected = model.isSeleted ?? false
callBack("")
}
override func layoutSubviews() {
super.layoutSubviews()
backImageView?.snp.makeConstraints({ make in
make.top.left.width.height.equalToSuperview()
})
backImageView?.layer.cornerRadius = 12
seletedBtn?.snp.makeConstraints({ make in
make.width.height.equalTo(24)
make.bottom.equalToSuperview().offset(-12)
make.right.equalToSuperview().offset(-20)
})
}
}
...@@ -42,6 +42,8 @@ class HomePayViewController:UIViewController { ...@@ -42,6 +42,8 @@ class HomePayViewController:UIViewController {
switch operstatus { switch operstatus {
case .close: case .close:
self.navigationController?.dismiss(animated: true) self.navigationController?.dismiss(animated: true)
default:
break
} }
} }
......
//
// BatteryMonitorManager.swift
// PhoneManager
//
// Created by edy on 2025/3/27.
//
import UIKit
protocol BatteryStatusDelegate: AnyObject {
func batteryStatusDidUpdate(level: Float, state: UIDevice.BatteryState)
}
class BatteryMonitorManager {
// Singleton instance
static let shared = BatteryMonitorManager()
// Delegate for status updates
weak var delegate: BatteryStatusDelegate?
// Current battery level (0.0 to 1.0)
private(set) var batteryLevel: Float = 0.0
// Current battery state
private(set) var batteryState: UIDevice.BatteryState = .unknown
// Flag to track if monitoring is active
private var isMonitoring = false
private init() {
// Private initializer for singleton
}
// MARK: - Public Methods
/// Start monitoring battery status
func startMonitoring() {
guard !isMonitoring else { return }
UIDevice.current.isBatteryMonitoringEnabled = true
// Get initial values
updateBatteryStatus()
// Register for notifications
NotificationCenter.default.addObserver(
self,
selector: #selector(batteryLevelDidChange),
name: UIDevice.batteryLevelDidChangeNotification,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(batteryStateDidChange),
name: UIDevice.batteryStateDidChangeNotification,
object: nil
)
isMonitoring = true
print("Battery monitoring started")
}
/// Stop monitoring battery status
func stopMonitoring() {
guard isMonitoring else { return }
NotificationCenter.default.removeObserver(self)
UIDevice.current.isBatteryMonitoringEnabled = false
isMonitoring = false
print("Battery monitoring stopped")
}
/// Get current battery status as a formatted string
// func currentBatteryStatus() -> String {
// let levelPercent = Int(batteryLevel * 100)
// let stateString = batteryStateDescription()
//
// return "Battery: \(levelPercent)% (\(stateString))"
// }
func getBatteryIsCharging() -> Bool {
UIDevice.current.isBatteryMonitoringEnabled = true
return UIDevice.current.batteryState == .charging
}
// MARK: - Private Methods
@objc private func batteryLevelDidChange() {
updateBatteryStatus()
}
@objc private func batteryStateDidChange() {
updateBatteryStatus()
}
private func updateBatteryStatus() {
batteryLevel = UIDevice.current.batteryLevel
batteryState = UIDevice.current.batteryState
// Notify delegate
delegate?.batteryStatusDidUpdate(level: batteryLevel, state: batteryState)
// Print to console (for debugging)
}
private func batteryStateDescription() -> String {
switch batteryState {
case .unknown:
return "Unknown"
case .unplugged:
return "Unplugged"
case .charging:
return "Charging"
case .full:
return "Full"
@unknown default:
return "Unknown state"
}
}
deinit {
stopMonitoring()
}
}
//
// NetStatusManager.swift
// PhoneManager
//
// Created by edy on 2025/3/27.
//
import Alamofire
import Network
enum NetStatus{
case NoNet
case WIFI
case WWAN
}
class NetStatusManager: NSObject {
static let manager:NetStatusManager = NetStatusManager()
private let monitor = NWPathMonitor()
private let queue = DispatchQueue(label: "NetworkMonitorQueue")
var currentStatus:NetStatus?
func startNet(netStatus: @escaping(NetStatus)->Void) {
monitor.pathUpdateHandler = { path in
if path.status == .satisfied {
Print("网络连接正常")
if path.isExpensive {
self.currentStatus = .WWAN
netStatus(.WWAN)
} else {
self.currentStatus = .WIFI
netStatus(.WIFI)
}
} else {
self.currentStatus = .NoNet
netStatus(.NoNet)
}
}
monitor.start(queue: queue)
}
func stop() {
monitor.cancel()
}
}
import Foundation
import SystemConfiguration.CaptiveNetwork
func getWiFiAddress() -> String? {
var address: String?
var ifaddr: UnsafeMutablePointer<ifaddrs>?
// 获取网络接口列表
guard getifaddrs(&ifaddr) == 0 else { return nil }
guard let firstAddr = ifaddr else { return nil }
// 遍历接口列表
for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
let interface = ifptr.pointee
let addrFamily = interface.ifa_addr.pointee.sa_family
// 检查接口是否为 IPv4 或 IPv6
if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {
let name = String(cString: interface.ifa_name)
// 筛选出 Wi-Fi (en0) 接口的 IP 地址
if name == "en0" {
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
&hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST)
address = String(cString: hostname)
}
}
}
freeifaddrs(ifaddr)
return address
}
//
// OpenCVWrapper.h
// PhoneManager
//
// Created by edy on 2025/3/25.
//
#ifdef __OBJC__
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface OpenCVWrapper : NSObject
/// 是否相似
+ (BOOL)areImagesSimilar:(UIImage *)image1 withImage2:(UIImage *)image2 threshold:(double)threshold;
/// 获取相似度
+ (double)compareImageSimilarity:(UIImage *)image1 withImage2:(UIImage *)image2;
@end
#endif
//
// OpenCVWrapper.m
// PhoneManager
//
// Created by edy on 2025/3/25.
//
#import "OpenCVWrapper.h"
#import <opencv2/core.hpp>
#import <opencv2/imgproc.hpp>
#import <opencv2/features2d.hpp>
@implementation OpenCVWrapper
+ (cv::Mat)cvMatFromUIImage:(UIImage *)image {
CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);
CGFloat cols = image.size.width;
CGFloat rows = image.size.height;
cv::Mat cvMat(rows, cols, CV_8UC4); // 8 bits per component, 4 channels (RGBA)
CGContextRef contextRef = CGBitmapContextCreate(cvMat.data, // Pointer to data
cols, // Width of bitmap
rows, // Height of bitmap
8, // Bits per component
cvMat.step[0], // Bytes per row
colorSpace, // Colorspace
kCGImageAlphaNoneSkipLast |
kCGBitmapByteOrderDefault); // Bitmap info flags
CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage);
CGContextRelease(contextRef);
return cvMat;
}
+ (double)compareImageSimilarity:(UIImage *)image1 withImage2:(UIImage *)image2 {
@try {
// 检查输入
if (!image1 || !image2) {
return 0.0;
}
// 转换为 OpenCV 格式
cv::Mat mat1 = [self cvMatFromUIImage:image1];
cv::Mat mat2 = [self cvMatFromUIImage:image2];
// 检查矩阵是否为空
if (mat1.empty() || mat2.empty()) {
return 0.0;
}
// 转换为灰度图
cv::Mat gray1, gray2;
cv::cvtColor(mat1, gray1, cv::COLOR_RGBA2GRAY);
cv::cvtColor(mat2, gray2, cv::COLOR_RGBA2GRAY);
// 调整大小
cv::Size size(256, 256);
cv::resize(gray1, gray1, size);
cv::resize(gray2, gray2, size);
// 计算直方图
cv::Mat hist1, hist2;
int channels[] = {0};
int histSize[] = {256};
float range[] = {0, 256};
const float* ranges[] = {range};
cv::calcHist(&gray1, 1, channels, cv::Mat(), hist1, 1, histSize, ranges);
cv::calcHist(&gray2, 1, channels, cv::Mat(), hist2, 1, histSize, ranges);
// 归一化直方图
cv::normalize(hist1, hist1, 0, 1, cv::NORM_MINMAX);
cv::normalize(hist2, hist2, 0, 1, cv::NORM_MINMAX);
// 计算相似度
double similarity = cv::compareHist(hist1, hist2, cv::HISTCMP_CORREL);
// 确保返回值在 0-1 之间
return std::max(0.0, std::min(1.0, similarity));
}
@catch (NSException *exception) {
NSLog(@"Error comparing images: %@", exception);
return 0.0;
}
}
+ (BOOL)areImagesSimilar:(UIImage *)image1 withImage2:(UIImage *)image2 threshold:(double)threshold {
@try {
if (!image1 || !image2) {
return NO;
}
double similarity = [self compareImageSimilarity:image1 withImage2:image2];
return similarity >= threshold;
}
@catch (NSException *exception) {
NSLog(@"Error comparing images: %@", exception);
return NO;
}
}
@end
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#import "OpenCVWrapper.h"
//
// UserActivityManager.swift
// PhoneManager
//
// Created by edy on 2025/3/26.
//
import AppIntents
@available(iOS 16, *)
struct LaunchAppIntent: AppIntent {
static var title: LocalizedStringResource = "打开应用"
static var description: IntentDescription = IntentDescription("打开并启动应用")
// 执行操作
func perform() async throws -> some IntentResult {
// 这里可以添加启动应用后的具体操作
return .result()
}
}
@available(iOS 16, *)
struct AppShortcuts: AppShortcutsProvider {
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: LaunchAppIntent(),
phrases: [
"打开 \(.applicationName)",
"启动 \(.applicationName)",
"运行 \(.applicationName)"
],
systemImageName: "app.fill"
)
}
}
...@@ -43,7 +43,7 @@ let whiteColor:String = "#ffffff" ...@@ -43,7 +43,7 @@ let whiteColor:String = "#ffffff"
let mColor:String = "#0082FF" let mColor:String = "#0082FF"
let navBack:String = "#0F0F0F" let navBack:String = "#ffffff"
let blackColor:String = "#000000" let blackColor:String = "#000000"
......
...@@ -24,6 +24,36 @@ let cWindow:UIWindow? = { ...@@ -24,6 +24,36 @@ let cWindow:UIWindow? = {
}() }()
func formatFileSize(_ bytes: Double) -> String {
let units = ["B", "KB", "MB", "GB", "TB"]
var size = bytes
var unitIndex = 0
// 循环计算合适的单位
while size >= 1024 && unitIndex < units.count - 1 {
size /= 1024
unitIndex += 1
}
// 格式化输出
return String(format: "%.2f %@", size, units[unitIndex])
}
func timeFormatter() -> String {
let formatter = DateFormatter()
formatter.dateFormat = "h:mma" // 注意这里的格式
formatter.amSymbol = "AM"
formatter.pmSymbol = "PM"
return formatter.string(from: Date())
}
func weekFormatter() -> String {
let formatter = DateFormatter()
formatter.dateFormat = "EEEE, MMMM d" // 自定义格式
return formatter.string(from: Date())
}
public func Print(_ items: Any...) { public func Print(_ items: Any...) {
#if DEBUG #if DEBUG
......
...@@ -15,6 +15,7 @@ enum AnimationStatus { ...@@ -15,6 +15,7 @@ enum AnimationStatus {
enum OperStatus { enum OperStatus {
case close case close
case delete
} }
enum CommonPush { enum CommonPush {
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>NSPhotoLibraryUsageDescription</key>
<string>We need album permission to access image storage information</string>
<key>NSAppTransportSecurity</key> <key>NSAppTransportSecurity</key>
<dict> <dict>
<key>NSAllowsArbitraryLoads</key> <key>NSAllowsArbitraryLoads</key>
...@@ -11,8 +9,10 @@ ...@@ -11,8 +9,10 @@
<key>NSAllowsArbitraryLoadsInWebContent</key> <key>NSAllowsArbitraryLoadsInWebContent</key>
<true/> <true/>
</dict> </dict>
<key>NSLocalNetworkUsageDescription</key> <key>NSUserActivityTypes</key>
<string>We need to access the network to load content</string> <array>
<string>LaunchAppIntent</string>
</array>
<key>UIApplicationSceneManifest</key> <key>UIApplicationSceneManifest</key>
<dict> <dict>
<key>UIApplicationSupportsMultipleScenes</key> <key>UIApplicationSupportsMultipleScenes</key>
......
...@@ -9,5 +9,6 @@ target 'PhoneManager' do ...@@ -9,5 +9,6 @@ target 'PhoneManager' do
pod 'Alamofire' pod 'Alamofire'
pod 'lottie-ios' pod 'lottie-ios'
pod 'SnapKit' pod 'SnapKit'
pod 'OpenCV', '~> 4.0.0'
end end
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