Commit b3db5b71 authored by shenyong's avatar shenyong

Merge branch 'develop_0409' into Adv_NewClean

parents 3349dd14 7550e2e4
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "tinted"
}
],
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "pm_battery_widgets_blue.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "pm_battery_widgets_blue@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "pm_battery_widgets_blue@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "pm_battery_widgets_small.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "pm_battery_widgets_small@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "pm_battery_widgets_small@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "pm_storage_widgets_blue.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "pm_storage_widgets_blue@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "pm_storage_widgets_blue@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "pm_storage_widgets_small.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "pm_storage_widgets_small@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "pm_storage_widgets_small@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
<?xml version="1.0" encoding="UTF-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>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.widgetkit-extension</string>
</dict>
</dict>
</plist>
//
// LockSreen.swift
// LockSreen
//
// Created by edy on 2025/5/12.
//
import WidgetKit
import SwiftUI
struct lockEmpty:View {
var body: some View {
ZStack(content: { })
}
}
// 正方形
struct LockSreenSquareView : View {
var entry:LockSreenProvider.Entry
var type:lockSreenType = lockSreenType()
var body: some View {
GeometryReader(content: { geometry in
let width = min(geometry.size.width, geometry.size.height)
ZStack {
let img = (type.isStorage ? "pm_storage_widgets_blue" : "pm_battery_widgets_blue")
let degree = (type.isStorage ? Double(entry.widgetUseSpace)/Double(entry.widgetAllSpace) * 360.0 : Double(entry.widgetBattery))
Image(img, bundle: nil).aspectRatio(contentMode: .fill)
lockArcShape(startAngle: .degrees(-90), endAngle:.degrees(degree-90)).stroke(style: StrokeStyle(lineWidth: 4, lineCap: .round))
}.frame(width: width, height: width)
})
}
}
// 长方形
struct LockSreenRectView : View {
var entry:LockSreenProvider.Entry
var type:lockSreenType = lockSreenType()
var body: some View {
let use = (Double(entry.widgetUseSpace)/Double(entry.widgetAllSpace))
HStack(content: {
VStack(spacing: 10, content: {
let name = type.isStorage ? "Storage" : "Battery"
let use = type.isStorage ? String("\(Int(use*100))%") : String("\(entry.widgetBattery)%")
let descp = type.isStorage ? String(" \(entry.widgetUseSpace)/\(entry.widgetAllSpace)GB") : entry.isCharging ? "Charging" : "No action"
VStack(spacing: 2, content: {
Text(name).font(.system(size: 12)).frame(maxWidth: .infinity,alignment:.leading)
Text(use).frame(maxWidth: .infinity, alignment: .leading).font(.system(size: 16,weight: .bold))
})
Text(descp).font(.system(size: 10)).frame(maxWidth: .infinity, alignment: .leading)
})
ZStack {
let img = (type.isStorage ? "pm_storage_widgets_blue" : "pm_battery_widgets_blue")
let degree = (type.isStorage ? Double(entry.widgetUseSpace)/Double(entry.widgetAllSpace) * 360 : Double(entry.widgetBattery))
Image(img, bundle: nil).aspectRatio(contentMode: .fill)
lockArcShape(startAngle: .degrees(-90), endAngle: .degrees( degree-90)).stroke(style: StrokeStyle(lineWidth: 4, lineCap: .round))
}
})
}
}
// 二者长方形
struct LockSreenBothView : View {
var entry:LockSreenProvider.Entry
var body: some View {
let use = (Double(entry.widgetUseSpace)/Double(entry.widgetAllSpace))
VStack(content: {
HStack(spacing: 5, content: {
Image("pm_battery_widgets_small")
Text("Battery").frame(maxWidth: .infinity, alignment: .leading).font(.system(size: 12,weight: .regular))
Text(String("\(entry.widgetBattery)%")).font(.system(size: 12,weight: .medium))
})
HStack(spacing: 3, content: {
Image("pm_storage_widgets_small")
Text("Storage").frame(maxWidth: .infinity, alignment: .leading).font(.system(size: 12,weight: .regular))
Text(String("\(Int(use*100))%")).font(.system(size: 12,weight: .medium))
})
})
.padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
}
}
struct LockSreenEntryView : View {
var type:lockSreenType = lockSreenType()
var entry: LockSreenProvider.Entry
@Environment(\.widgetFamily) var family
var body: some View {
switch family {
case .accessoryCircular:
LockSreenSquareView(entry: entry ,type: type)
case .accessoryRectangular:
LockSreenRectView(entry: entry ,type: type)
default:
lockEmpty()
}
}
}
// 圆孤
struct lockArcShape: Shape {
var startAngle: Angle
var endAngle: Angle
func path(in rect: CGRect) -> Path {
var path = Path()
let center = CGPoint(x: rect.midX, y: rect.midY)
let radius = min(rect.width, rect.height) / 2.0 - 2
path.addArc(
center: center,
radius: radius,
startAngle: startAngle,
endAngle: endAngle,
clockwise: false
)
return path
}
}
//
// LockSreenBundle.swift
// LockSreen
//
// Created by edy on 2025/5/12.
//
import WidgetKit
import SwiftUI
let kind: String = "LockSreen"
let kind1: String = "LockSreen1"
@main
struct LockSreenBundle: WidgetBundle {
var body: some Widget {
LockStorageSreen(type: lockSreenType(isStorage: true, kind: kind, displayName: "Storage", description: "Monitor your storage space"))
LockStorageSreen(type: lockSreenType(kind: kind1, displayName: "Battery", description: "Monitor your battery"))
LockBothSreen()
}
}
struct LockStorageSreen: Widget {
var type:lockSreenType = lockSreenType()
var body: some WidgetConfiguration {
StaticConfiguration(kind: type.kind, provider: LockSreenProvider()) { entry in
if #available(iOS 17.0, *) {
LockSreenEntryView(type: type, entry: entry)
.containerBackground(.fill.tertiary, for: .widget)
} else {
LockSreenEntryView(type: type, entry: entry)
.padding()
.background(Color.clear)
}
}
.configurationDisplayName(type.displayName)
.description(type.description)
.supportedFamilies([.accessoryCircular,.accessoryRectangular])
}
}
struct LockBothSreen: Widget {
let kind: String = "LockSreenBoth"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: LockSreenProvider()) { entry in
if #available(iOS 17.0, *) {
LockSreenBothView(entry: entry)
.containerBackground(.fill.tertiary, for: .widget)
} else {
LockSreenBothView(entry: entry)
.background(Color.clear)
}
}
.configurationDisplayName("Both")
.description("Monitor your battery and storage space")
.supportedFamilies([.accessoryRectangular])
}
}
//
// LockSreenModel.swift
// widget
//
// Created by Flower on 2025/5/10.
//
import WidgetKit
import SwiftUI
struct LockSreenProvider: TimelineProvider {
func placeholder(in context: Context) -> LockSreenEntry {
LockSreenEntry(date: .now, widgets: 0, widgetBattery: 0, widgetStorage: 0, widgetAllSpace: 0, widgetUseSpace: 0, batteryState: "Off", isCharging: false)
}
func getSnapshot(in context: Context, completion: @escaping (LockSreenEntry) -> ()) {
let entry = LockSreenEntry(date: .now, widgets: 0, widgetBattery: 0, widgetStorage: 0, widgetAllSpace: 0, widgetUseSpace: 0, batteryState: "Off", isCharging: false)
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<LockSreenEntry>) -> ()) {
let entry = loadSharedData()
let timeline = Timeline(entries: [entry], policy: .after(Date().addingTimeInterval(60)))
completion(timeline)
}
private func loadSharedData() -> LockSreenEntry {
guard let sharedDefaults = UserDefaults(suiteName: "group.com.app.phonemanager"),
let data:Data = sharedDefaults.object(forKey: "widgetSharedData") as? Data,
let decodedData = try? JSONDecoder().decode(WidgetData.self, from: data)
else {
return LockSreenEntry(date: Date(), widgets: 0, widgetBattery: 0, widgetStorage: 0 ,widgetAllSpace: 0 ,widgetUseSpace: 0 ,batteryState: "Off" , isCharging: false)
}
return LockSreenEntry(date: Date(), widgets: decodedData.widget, widgetBattery: decodedData.battery, widgetStorage: decodedData.widgetStorage,widgetAllSpace: decodedData.AllSpace,widgetUseSpace: decodedData.UseSpace ,batteryState: decodedData.batteryState ,isCharging: decodedData.isCharging)
}
}
struct WidgetData: Codable {
var userId: String
var widget: Int
var battery: Int
var widgetStorage: Int
var AllSpace: Int
var UseSpace: Int
let batteryState: String
let isCharging: Bool
}
struct LockSreenEntry: TimelineEntry {
let date: Date
var widgets:Int
let widgetBattery:Int
let widgetStorage:Int
let widgetAllSpace:Int
let widgetUseSpace:Int
let batteryState: String // 是否低电量模式
let isCharging: Bool // 是否在充电
}
// 单一
struct lockSreenType {
var type:Int = 0 // 正方形 长方形
var isStorage:Bool = false // 是否是存储
var kind:String = ""
var displayName:String = ""
var description:String = ""
}
<?xml version="1.0" encoding="UTF-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>com.apple.security.application-groups</key>
<array>
<string>group.com.app.phonemanager</string>
</array>
</dict>
</plist>
......@@ -7,6 +7,9 @@
objects = {
/* Begin PBXBuildFile section */
04197BE82DD1B9DB009FE068 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04BD915D2D9D68AD00055CEB /* WidgetKit.framework */; };
04197BE92DD1B9DB009FE068 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04BD915F2D9D68AD00055CEB /* SwiftUI.framework */; };
04197BF42DD1B9DC009FE068 /* LockSreenExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 04197BE72DD1B9DB009FE068 /* LockSreenExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
0496DEF32D9E3F58005B2834 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04BD915D2D9D68AD00055CEB /* WidgetKit.framework */; };
0496DEF42D9E3F58005B2834 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04BD915F2D9D68AD00055CEB /* SwiftUI.framework */; };
0496DEFF2D9E3F59005B2834 /* widgetExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 0496DEF22D9E3F57005B2834 /* widgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
......@@ -19,6 +22,13 @@
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
04197BF22DD1B9DC009FE068 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = EB388E532D8A61A800629B0D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 04197BE62DD1B9DB009FE068;
remoteInfo = LockSreenExtension;
};
0496DEFD2D9E3F59005B2834 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = EB388E532D8A61A800629B0D /* Project object */;
......@@ -54,6 +64,7 @@
files = (
0496DEFF2D9E3F59005B2834 /* widgetExtension.appex in Embed Foundation Extensions */,
04CF31772DA7E790001C87CA /* ChargeShow.appex in Embed Foundation Extensions */,
04197BF42DD1B9DC009FE068 /* LockSreenExtension.appex in Embed Foundation Extensions */,
);
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
......@@ -61,6 +72,8 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
04197BE72DD1B9DB009FE068 /* LockSreenExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = LockSreenExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
04197BF92DD1BA4C009FE068 /* LockSreenExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = LockSreenExtension.entitlements; sourceTree = "<group>"; };
0496DEF22D9E3F57005B2834 /* widgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = widgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
0496DF042D9E3FA7005B2834 /* widgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = widgetExtension.entitlements; sourceTree = "<group>"; };
04BBB4E52DC0748F00D7E3AB /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
......@@ -77,6 +90,13 @@
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
04197BF82DD1B9DC009FE068 /* Exceptions for "LockSreen" folder in "LockSreenExtension" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
Info.plist,
);
target = 04197BE62DD1B9DB009FE068 /* LockSreenExtension */;
};
041E22DC2DCCD63B0028E917 /* Exceptions for "widget" folder in "PhoneManager" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
......@@ -108,6 +128,14 @@
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
/* Begin PBXFileSystemSynchronizedRootGroup section */
04197BEA2DD1B9DB009FE068 /* LockSreen */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
04197BF82DD1B9DC009FE068 /* Exceptions for "LockSreen" folder in "LockSreenExtension" target */,
);
path = LockSreen;
sourceTree = "<group>";
};
0496DEF52D9E3F58005B2834 /* widget */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
......@@ -136,6 +164,15 @@
/* End PBXFileSystemSynchronizedRootGroup section */
/* Begin PBXFrameworksBuildPhase section */
04197BE42DD1B9DB009FE068 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
04197BE92DD1B9DB009FE068 /* SwiftUI.framework in Frameworks */,
04197BE82DD1B9DB009FE068 /* WidgetKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
0496DEEF2D9E3F57005B2834 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
......@@ -190,11 +227,13 @@
EB388E522D8A61A800629B0D = {
isa = PBXGroup;
children = (
04197BF92DD1BA4C009FE068 /* LockSreenExtension.entitlements */,
04CF317C2DA7E7BE001C87CA /* Intents.intentdefinition */,
0496DF042D9E3FA7005B2834 /* widgetExtension.entitlements */,
EB388E5D2D8A61A800629B0D /* PhoneManager */,
0496DEF52D9E3F58005B2834 /* widget */,
04CF31712DA7E78F001C87CA /* ChargeShow */,
04197BEA2DD1B9DB009FE068 /* LockSreen */,
EB388E5C2D8A61A800629B0D /* Products */,
CB2ACD1E9442B4500087E831 /* Pods */,
27ECDADD9059AB5043B8E1E9 /* Frameworks */,
......@@ -207,6 +246,7 @@
EB388E5B2D8A61A800629B0D /* PhoneManager.app */,
0496DEF22D9E3F57005B2834 /* widgetExtension.appex */,
04CF316F2DA7E78F001C87CA /* ChargeShow.appex */,
04197BE72DD1B9DB009FE068 /* LockSreenExtension.appex */,
);
name = Products;
sourceTree = "<group>";
......@@ -214,6 +254,28 @@
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
04197BE62DD1B9DB009FE068 /* LockSreenExtension */ = {
isa = PBXNativeTarget;
buildConfigurationList = 04197BF52DD1B9DC009FE068 /* Build configuration list for PBXNativeTarget "LockSreenExtension" */;
buildPhases = (
04197BE32DD1B9DB009FE068 /* Sources */,
04197BE42DD1B9DB009FE068 /* Frameworks */,
04197BE52DD1B9DB009FE068 /* Resources */,
);
buildRules = (
);
dependencies = (
);
fileSystemSynchronizedGroups = (
04197BEA2DD1B9DB009FE068 /* LockSreen */,
);
name = LockSreenExtension;
packageProductDependencies = (
);
productName = LockSreenExtension;
productReference = 04197BE72DD1B9DB009FE068 /* LockSreenExtension.appex */;
productType = "com.apple.product-type.app-extension";
};
0496DEF12D9E3F57005B2834 /* widgetExtension */ = {
isa = PBXNativeTarget;
buildConfigurationList = 0496DF022D9E3F59005B2834 /* Build configuration list for PBXNativeTarget "widgetExtension" */;
......@@ -272,6 +334,7 @@
dependencies = (
0496DEFE2D9E3F59005B2834 /* PBXTargetDependency */,
04CF31762DA7E78F001C87CA /* PBXTargetDependency */,
04197BF32DD1B9DC009FE068 /* PBXTargetDependency */,
);
fileSystemSynchronizedGroups = (
EB388E5D2D8A61A800629B0D /* PhoneManager */,
......@@ -291,6 +354,9 @@
LastSwiftUpdateCheck = 1620;
LastUpgradeCheck = 1620;
TargetAttributes = {
04197BE62DD1B9DB009FE068 = {
CreatedOnToolsVersion = 16.2;
};
0496DEF12D9E3F57005B2834 = {
CreatedOnToolsVersion = 16.2;
};
......@@ -320,11 +386,19 @@
EB388E5A2D8A61A800629B0D /* PhoneManager */,
0496DEF12D9E3F57005B2834 /* widgetExtension */,
04CF316E2DA7E78F001C87CA /* ChargeShow */,
04197BE62DD1B9DB009FE068 /* LockSreenExtension */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
04197BE52DD1B9DB009FE068 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
0496DEF02D9E3F57005B2834 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
......@@ -408,6 +482,13 @@
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
04197BE32DD1B9DB009FE068 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
0496DEEE2D9E3F57005B2834 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
......@@ -434,6 +515,11 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
04197BF32DD1B9DC009FE068 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 04197BE62DD1B9DB009FE068 /* LockSreenExtension */;
targetProxy = 04197BF22DD1B9DC009FE068 /* PBXContainerItemProxy */;
};
0496DEFE2D9E3F59005B2834 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 0496DEF12D9E3F57005B2834 /* widgetExtension */;
......@@ -447,6 +533,72 @@
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
04197BF62DD1B9DC009FE068 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_ENTITLEMENTS = LockSreenExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 6K23946NQ5;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = LockSreen/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = LockSreen;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.app.phonemanager.lockSreenWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
04197BF72DD1B9DC009FE068 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_ENTITLEMENTS = LockSreenExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 6K23946NQ5;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = LockSreen/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = LockSreen;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.app.phonemanager.lockSreenWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
0496DF002D9E3F59005B2834 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
......@@ -792,6 +944,15 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
04197BF52DD1B9DC009FE068 /* Build configuration list for PBXNativeTarget "LockSreenExtension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
04197BF62DD1B9DC009FE068 /* Debug */,
04197BF72DD1B9DC009FE068 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
0496DF022D9E3F59005B2834 /* Build configuration list for PBXNativeTarget "widgetExtension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
......
......@@ -10,6 +10,7 @@ import AppIntents
import Photos
import GoogleMobileAds
import UserMessagingPlatform
import GoogleSignIn
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
......@@ -45,6 +46,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// 初始化广告SDK
AdvManager.shared.initAdertisementSDK()
PMEmailManager.shareManager.restore()
PhotoManager.shared.config()
......@@ -337,7 +339,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}
func application(
_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool {
return GIDSignIn.sharedInstance.handle(url)
}
}
{
"images" : [
{
"filename" : "Group_1171275119.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Group_1171275119@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Group_1171275119@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "Group_1171275120.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Group_1171275120@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Group_1171275120@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "image_76.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "image_76@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "image_76@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "img_phone_battery_02.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "img_phone_battery_02@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "img_phone_battery_02@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "ic_ok_duolicates.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ic_ok_duolicates@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ic_ok_duolicates@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "icon_close.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon_close@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon_close@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "Frame.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Frame@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Frame@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "Frame.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Frame@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Frame@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "Frame_1171276289.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Frame_1171276289@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Frame_1171276289@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
......@@ -12,7 +12,7 @@ class ChargeGuideController : BaseViewController,UIScrollViewDelegate,UINavigati
let imageString : [String] = [
"img_phone_battery_02",
"img_phone_battery_02 1",
"img_phone_battery_03",
"img_phone_battery_04",
"img_phone_battery_05",
......
......@@ -13,7 +13,7 @@ class ChargeGuideStartController : BaseViewController {
lazy var backView : UIImageView = {
let view = UIImageView()
view.image = UIImage(named: "img_bj_battery")
view.image = UIImage(named: "image_76")
return view
}()
......@@ -40,7 +40,7 @@ class ChargeGuideStartController : BaseViewController {
lazy var chargeView : UIImageView = {
let view = UIImageView()
view.image = UIImage(named: "img_phone_battery_01")
view.image = UIImage(named: "Group_1171275119")
return view
}()
......@@ -96,15 +96,15 @@ class ChargeGuideStartController : BaseViewController {
self.startButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(15)
make.right.equalToSuperview().offset(-15)
make.top.equalTo(self.chargeView.snp.bottom)
make.bottom.equalToSuperview().offset(-safeHeight-37)
make.height.equalTo(50)
}
self.chargeView.snp.makeConstraints { make in
make.top.equalTo(self.detailTipTitleLabel.snp.bottom).offset(42)
make.height.equalTo(476)
make.left.equalToSuperview().offset(27)
make.right.equalToSuperview().offset(-26)
make.top.equalTo(self.detailTipTitleLabel.snp.bottom).offset(32)
make.height.equalTo(453.5 * RScreenH())
make.width.equalTo(345 * RScreenH())
make.centerX.equalToSuperview()
}
self.closeButton.snp.makeConstraints { make in
......
......@@ -12,7 +12,7 @@ class ChargeGuideEndView : UIView{
lazy var backView : UIImageView = {
let view = UIImageView()
view.image = UIImage(named: "img_bj_battery")
view.image = UIImage(named: "image_76")
return view
}()
......@@ -39,7 +39,7 @@ class ChargeGuideEndView : UIView{
lazy var chargeView : UIImageView = {
let view = UIImageView()
view.image = UIImage(named: "img_phone_battery_08")
view.image = UIImage(named: "Group_1171275120")
return view
}()
......@@ -88,15 +88,15 @@ class ChargeGuideEndView : UIView{
self.startButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(15)
make.right.equalToSuperview().offset(-15)
make.top.equalTo(self.chargeView.snp.bottom).offset(66)
make.bottom.equalToSuperview().offset(-safeHeight-37)
make.height.equalTo(50)
}
self.chargeView.snp.makeConstraints { make in
make.top.equalTo(self.detailTipTitleLabel.snp.bottom).offset(42)
make.height.equalTo(352)
make.left.equalToSuperview().offset(27)
make.right.equalToSuperview().offset(-26)
make.height.equalTo(445 * RScreenH())
make.width.equalTo(338 * RScreenH())
make.centerX.equalToSuperview()
}
}
......
......@@ -15,7 +15,7 @@ class ChargeGuideNormalView : UIView{
lazy var backView : UIImageView = {
let view = UIImageView()
view.image = UIImage(named: "img_bj_battery")
view.image = UIImage(named: "image_76")
return view
}()
......
......@@ -62,8 +62,7 @@ class HomeViewController:BaseViewController {
case 3 :
DispatchQueue.main.async {[weak self] in
guard let self else {return}
//FIXME: 是否登录了谷歌邮箱
if false {
if PMEmailManager.shareManager.loginUser != nil {
let vc:EmailCleanController = EmailCleanController()
self.navigationController?.pushViewController(vc, animated: true)
}else{
......
//
// DateSelectButtonView.swift
// PhoneManager
//
// Created by edy on 2025/5/12.
//
import Foundation
class DateSelectButtonView : UIView {
var type : PikerDateType?
lazy var dateButton : UIButton = {
let button = UIButton(type: .custom)
button.setTitle("Start date", for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .semibold)
button.setTitleColor(UIColor(red: 0.07, green: 0.07, blue: 0.07, alpha: 1), for: .normal)
button.backgroundColor = UIColor(red: 0.9, green: 0.9, blue: 0.9, alpha: 1)
button.layer.borderColor = UIColor(red: 0.96, green: 0.96, blue: 0.96, alpha: 1).cgColor
button.layer.borderWidth = 1.0
button.layer.cornerRadius = 21
button.clipsToBounds = true
return button
}()
lazy var closeButton : UIButton = {
let button = UIButton(type: .custom)
button.setImage(UIImage(named: "ic_close_charging"), for: .normal)
button.addTarget(self, action: #selector(closeButtonAction), for: .touchUpInside)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.addSubview(dateButton)
self.addSubview(closeButton)
self.dateButton.snp.makeConstraints { make in
make.left.bottom.right.equalToSuperview()
make.top.equalToSuperview().offset(4)
}
self.closeButton.snp.makeConstraints { make in
make.width.height.equalTo(16)
make.top.equalToSuperview()
make.right.equalToSuperview().offset(-5)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func closeButtonAction(){
reSetButtonTitle()
}
func reSetButtonTitle() {
DispatchQueue.main.async {
let title = self.type == .start ? "Start date" : "Over date"
self.dateButton.setTitle(title, for: .normal)
self.closeButton.isHidden = true
}
}
}
......@@ -42,10 +42,19 @@ class HomeInfoView :UIView {
lazy var headerView:HomeInfoTitleView = {
let sview:HomeInfoTitleView = HomeInfoTitleView(frame: CGRect(x: 0, y: 0, width: width, height: 84))
sview.titleLabel.text = self.titleText
sview.filterButton.isHidden = self.type != .similar
tableView.addSubview(sview)
return sview
}()
lazy var nextRowButton : UIButton = {
let button = UIButton(type: .custom)
button.setImage(UIImage(named: "Frame_1171276289"), for: .normal)
button.backgroundColor = .clear
button.addTarget(self, action: #selector(nextRowButtonAction), for: .touchUpInside)
return button
}()
lazy var deleteView:HomeInfoDeleteView = {
let cH:CGFloat = 48 + 2 * marginLR + safeHeight
......@@ -101,6 +110,38 @@ class HomeInfoView :UIView {
super.init(frame: frame)
setupUI()
self.headerView.sortViewSubmitCallBack = {[weak self]filterModel in
guard let self else {return}
// 从源头获取相似数据
PhotoDataManager.manager.loadFromFileSystem(resultModel: {[weak self] model in
guard let self else {return}
let tempData = self.filterDataByDate(orgModels: model.titleModelArray[1].assets, startDate: filterModel.startDate, endDate: filterModel.endDate)
// 重新更新下数据源
self.ids = self.sortData(source: tempData, type: filterModel.sortType)
var tempModels : [HomeInfoTableItem] = []
for array in self.ids ?? [] {
var smodels:[ImageSeletedCollectionItem] = []
for id in array {
let smodel = ImageSeletedCollectionItem()
smodel.id = id
smodel.isSeleted = false
smodels.append(smodel)
}
let smodel = HomeInfoTableItem()
smodel.type = type
smodel.smodels = smodels
smodel.titleText = titleText
tempModels.append(smodel)
}
models = tempModels
DispatchQueue.main.async {
// FIXME: 闪屏
self.tableView.reloadSections(IndexSet(integer: 0), with: .automatic)
}
})
}
}
required init?(coder: NSCoder) {
......@@ -116,6 +157,15 @@ class HomeInfoView :UIView {
self.addSubview(tableView)
self.addSubview(deleteView)
self.addSubview(self.nextRowButton)
self.nextRowButton.snp.makeConstraints { make in
make.bottom.equalToSuperview().offset(-safeHeight - 106)
make.right.equalToSuperview().offset(-16)
make.height.equalTo(38)
make.width.equalTo(24)
}
}
func changeValue() {
......@@ -253,6 +303,26 @@ class HomeInfoView :UIView {
extension HomeInfoView:UITableViewDataSource,UITableViewDelegate {
@objc func nextRowButtonAction(){
self.tableScrollToNextRow()
}
/// 让表格自动滚动一行
private func tableScrollToNextRow(){
if let indexPath = tableView.indexPathsForVisibleRows?.first {
// 计算下一行的索引
let nextRow = indexPath.row + 1
let nextSection = indexPath.section
// 检查下一行是否存在
if nextRow < tableView.numberOfRows(inSection: nextSection) {
let nextIndexPath = IndexPath(row: nextRow, section: nextSection)
// 滚动到下一行
tableView.scrollToRow(at: nextIndexPath, at: .top, animated: true)
}
}
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let OffsetY = scrollView.contentOffset.y
let top = CGRectGetMaxY(self.headerView.titleLabel.frame) + 8
......@@ -275,6 +345,7 @@ extension HomeInfoView:UITableViewDataSource,UITableViewDelegate {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: HomeInfoTableViewCell.identifier, for: indexPath) as! HomeInfoTableViewCell
cell.type = self.type
cell.model = models[indexPath.row]
......@@ -303,10 +374,62 @@ extension HomeInfoView:UITableViewDataSource,UITableViewDelegate {
return UIView()
}
func filterDataByDate(orgModels : [[AssetModel]], startDate:Date? ,endDate : Date?)->[[AssetModel]]{
var tempArray : [[AssetModel]] = []
for item in orgModels {
var array = item
if startDate != nil {
array = array.filter({$0.createDate > startDate!})
}
if endDate != nil {
array = array.filter({$0.createDate < endDate!})
}
if array.count > 2 {
tempArray.append(item)
}
}
return tempArray
}
func sortData(source: [[AssetModel]], type: ResouceSortType) -> [[AssetModel]] {
switch type {
case .largest:
return source.sorted { subArray1, subArray2 in
let sum1 = subArray1.reduce(0) { $0 + $1.assetSize }
let sum2 = subArray2.reduce(0) { $0 + $1.assetSize }
return sum1 > sum2
}
case .smallest:
return source.sorted { subArray1, subArray2 in
let sum1 = subArray1.reduce(0) { $0 + $1.assetSize }
let sum2 = subArray2.reduce(0) { $0 + $1.assetSize }
return sum1 < sum2
}
case .latest:
return source.sorted { subArray1, subArray2 in
guard let max1 = subArray1.max(by: { $0.createDate < $1.createDate })?.createDate else { return false }
guard let max2 = subArray2.max(by: { $0.createDate < $1.createDate })?.createDate else { return true }
return max1 > max2
}
case .oldest:
return source.sorted { subArray1, subArray2 in
guard let min1 = subArray1.min(by: { $0.createDate < $1.createDate })?.createDate else { return false }
guard let min2 = subArray2.min(by: { $0.createDate < $1.createDate })?.createDate else { return true }
return min1 < min2
}
}
}
}
class HomeInfoTitleView:UIView {
var sortViewSubmitCallBack : (ResourceFilterBoxModel)->Void = {model in}
lazy var titleLabel:UILabel = {
let sview:UILabel = UILabel()
......@@ -333,6 +456,23 @@ class HomeInfoTitleView:UIView {
return sview
}()
// 筛选按钮
lazy var filterButton : UIButton = {
let button = UIButton(type: .custom)
button.setImage(UIImage(named: "Frame 1"), for: .normal)
button.setTitle("The largest", for: .normal)
button.layer.cornerRadius = 14
button.clipsToBounds = true
button.backgroundColor = UIColor(red: 0, green: 0.51, blue: 1, alpha: 0.1000)
button.titleLabel?.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
button.setTitleColor(UIColor(red: 0.07, green: 0.07, blue: 0.07, alpha: 1), for: .normal)
button.addTarget(self, action: #selector(filterButtonAction), for: .touchUpInside)
button.isHidden = true
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
......@@ -348,6 +488,15 @@ class HomeInfoTitleView:UIView {
backgroundColor = .white
addSubview(titleLabel)
addSubview(numberLabel)
addSubview(self.filterButton)
self.filterButton.snp.makeConstraints { make in
make.top.equalToSuperview().offset(35)
make.right.equalToSuperview().offset(-15)
make.height.equalTo(28)
make.width.equalTo(98)
}
}
func changeContent(title:String,allNumber:Int,seletedCount:Int) {
......@@ -381,6 +530,20 @@ class HomeInfoTitleView:UIView {
numberLabel.attributedText = attributedString2
numberLabel.y = height - numberLabel.height - 8
}
@objc func filterButtonAction(){
if let cWindow = cWindow {
let filterView : ResourceFilterBoxView = ResourceFilterBoxView.init(frame: cWindow.bounds)
cWindow.addSubview(filterView)
filterView.submitCallBack = {model in
DispatchQueue.main.async {
self.filterButton.setTitle(model.sortType.rawValue, for: .normal)
}
self.sortViewSubmitCallBack(model)
}
}
}
}
......@@ -437,5 +600,7 @@ class HomeInfoDeleteView:UIView {
callBack(OperStatus.delete)
}
}
//
// ResourceFilterBoxView.swift
// PhoneManager
//
// Created by edy on 2025/5/12.
//
import Foundation
import SnapKit
enum ResouceSortType : String{
case largest = "The largest"
case smallest = "The smallest"
case latest = "The latest"
case oldest = "The oldest"
}
struct ResourceFilterBoxModel {
var startDate : Date?
var endDate : Date?
var sortType : ResouceSortType = .largest
init(startDate: Date? = nil, endDate: Date? = nil, sortType: ResouceSortType) {
self.startDate = startDate
self.endDate = endDate
self.sortType = sortType
}
}
class ResourceFilterBoxView : UIView {
var submitCallBack : (ResourceFilterBoxModel)->Void = {model in}
var startDate : Date?
var endDate : Date?
var currentIndex: Int = 0
let tableData: [String] = ["The largest", "The smallest", "The latest", "The oldest"]
var callBack : callBack<Any> = {sortType in}
var datePicker : YearMonthPickerView?
private var selectedViewBottomConstraint: Constraint?
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
lazy var backView: UIView = {
let view = UIView()
view.backgroundColor = UIColor(red: 0.07, green: 0.07, blue: 0.07, alpha: 0.8000)
view.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer()
tap.addTarget(self, action: #selector(backViewClick))
view.addGestureRecognizer(tap)
return view
}()
lazy var selectedView: UIView = {
let view = UIView()
view.backgroundColor = .white
return view
}()
lazy var startDateButton : DateSelectButtonView = {
let view = DateSelectButtonView()
view.type = .start
view.dateButton.addTarget(self , action: #selector(startDateButtonAction), for: .touchUpInside)
return view
}()
lazy var endDateButton : DateSelectButtonView = {
let view = DateSelectButtonView()
view.type = .end
view.dateButton.addTarget(self , action: #selector(endDateButtonAction), for: .touchUpInside)
return view
}()
lazy var titleLabel: UILabel = {
let view = UILabel()
view.text = "Sorting method"
view.textAlignment = .center
view.textColor = UIColor(red: 0.7, green: 0.7, blue: 0.7, alpha: 1)
view.font = UIFont.systemFont(ofSize: 14, weight: .bold)
return view
}()
lazy var tableView: UITableView = {
let view = UITableView()
view.register(ResourceFilterBoxTableViewCell.classForCoder(), forCellReuseIdentifier: "ResourceFilterBoxTableViewCell")
view.backgroundColor = .clear
view.separatorStyle = .none
view.showsVerticalScrollIndicator = false
view.delegate = self
view.dataSource = self
view.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
if #available(iOS 15.0, *) {
view.sectionHeaderTopPadding = 0
}
return view
}()
lazy var applyButton: UIButton = {
let view = UIButton(type: .custom)
view.setTitle("Continue", for: .normal)
view.layer.cornerRadius = 25
view.clipsToBounds = true
view.backgroundColor = UIColor(red: 0, green: 0.51, blue: 1, alpha: 1)
view.setTitleColor(.white, for: .normal)
view.addTarget(self, action: #selector(applyAction), for: .touchUpInside)
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
addViews()
setUpUI()
}
func getMonthEn(month: Int )-> String{
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US")
return formatter.monthSymbols[month]
}
override func removeFromSuperview() {
UIView.animate(withDuration: 0.3) {
// 更新约束
self.backView.backgroundColor = .clear
self.selectedViewBottomConstraint?.update(offset: 440)
self.layoutIfNeeded()
}completion: { _ in
super.removeFromSuperview()
}
}
func addViews(){
self.addSubview(self.backView)
self.backView.addSubview(self.selectedView)
self.selectedView.addSubview(self.startDateButton)
self.selectedView.addSubview(self.endDateButton)
self.selectedView.addSubview(self.titleLabel)
self.selectedView.addSubview(self.tableView)
self.selectedView.addSubview(self.applyButton)
self.startDateButton.reSetButtonTitle()
self.endDateButton.reSetButtonTitle()
}
func setUpUI(){
self.backView.snp.makeConstraints { make in
make.top.left.right.bottom.equalToSuperview()
}
self.selectedView.snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.height.equalTo(440)
// 初始时将 selectedView 移到屏幕下方
self.selectedViewBottomConstraint = make.bottom.equalToSuperview().offset(440).constraint
}
self.startDateButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(27 * RScreenW())
make.top.equalToSuperview().offset(24)
make.width.equalTo(151 * RScreenW())
make.height.equalTo(46)
}
self.endDateButton.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-27 * RScreenW())
make.top.equalToSuperview().offset(24)
make.width.equalTo(151 * RScreenW())
make.height.equalTo(46)
}
self.titleLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(15)
make.right.equalToSuperview().offset(-15)
make.top.equalTo(self.startDateButton.snp.bottom).offset(11)
make.height.equalTo(22)
}
self.applyButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(15)
make.right.equalToSuperview().offset(-15)
make.height.equalTo(50)
make.bottom.equalToSuperview().offset(-26)
}
self.tableView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(15)
make.right.equalToSuperview().offset(-15)
make.top.equalTo(self.titleLabel.snp.bottom).offset(14)
make.bottom.equalTo(self.applyButton.snp.top).offset(-23)
}
// 先显示背景,然后延迟显示弹框
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
UIView.animate(withDuration: 0.1) {
// 更新约束
self.selectedViewBottomConstraint?.update(offset: 0)
self.layoutIfNeeded()
}
}
}
@objc func applyAction() {
// 移除自身
self.dismissDatePicker()
self.removeFromSuperview()
self.submitCallBack(ResourceFilterBoxModel.init(startDate: self.startDate, endDate: self.endDate, sortType: getSortTypeByTag(tag:self.currentIndex)))
// 开始排序
}
func getSortTypeByTag(tag : Int) -> ResouceSortType{
var tempType : ResouceSortType = .largest
switch self.currentIndex {
case 0:
tempType = .largest
case 1:
tempType = .smallest
case 2:
tempType = .latest
case 3:
tempType = .oldest
default:
break
}
return tempType
}
func dateFrom(year: Int, month: Int) -> Date? {
var components = DateComponents()
components.year = year
components.month = month
components.day = 1 // 设置为当月第一天
// 使用公历日历
let calendar = Calendar(identifier: .gregorian)
return calendar.date(from: components)
}
func lastDayOfMonth(year: Int, month: Int) -> Date? {
// 计算下个月的年和月
var nextMonth = month + 1
var nextYear = year
if nextMonth > 12 {
nextMonth = 1
nextYear += 1
}
// 获取下个月第一天的日期
var components = DateComponents()
components.year = nextYear
components.month = nextMonth
components.day = 1
let calendar = Calendar(identifier: .gregorian)
guard let nextMonthFirstDay = calendar.date(from: components) else {
return nil
}
// 减去一天得到当月最后一天
return calendar.date(byAdding: .day, value: -1, to: nextMonthFirstDay)
}
@objc func backViewClick() {
// 移除自身
self.dismissDatePicker()
self.removeFromSuperview()
}
@objc func startDateButtonAction(_ sender : UIButton) {
popDatePikerView(type: .start)
}
@objc func endDateButtonAction(_ sender : UIButton) {
popDatePikerView(type: .end)
}
func popDatePikerView(type: PikerDateType){
showDatePicker(type: type)
}
// 显示
func showDatePicker(type: PikerDateType){
if self.datePicker != nil {
self.datePicker?.type = type
return
}
self.datePicker = YearMonthPickerView()
self.datePicker?.type = type
self.datePicker?.backgroundColor = .white
if let window = cWindow,let datePicker = self.datePicker {
let datePickerFrame = CGRect(x: 0, y: window.height, width:window.width, height: 204)
datePicker.frame = datePickerFrame
cWindow?.addSubview(datePicker)
UIView.animate(withDuration: 0.3, animations: {
datePicker.frame = CGRect(x: 0, y: window.height - 204, width: window.width, height: 204)
})
}
self.datePicker?.onCancel = {
self.dismissDatePicker()
}
self.datePicker?.onConfirm = {type,year,month in
DispatchQueue.main.async {
if type == .start {
self.startDateButton.dateButton.setTitle("From \(self.getMonthEn(month: month)) \(year)", for: .normal)
self.startDateButton.closeButton.isHidden = false
self.startDate = self.dateFrom(year: year, month: month)
}else {
self.endDateButton.dateButton.setTitle("To \(self.getMonthEn(month: month)) \(year)", for: .normal)
self.endDateButton.closeButton.isHidden = false
self.endDate = self.lastDayOfMonth(year: year, month: month)
}
}
self.dismissDatePicker()
}
}
// 消失
func dismissDatePicker(){
if let window = cWindow,let datePicker = self.datePicker {
UIView.animate(withDuration: 0.2, animations: {
datePicker.frame = CGRect(x: 0, y: window.height, width: window.width, height: 204)
}) { finished in
datePicker.removeFromSuperview()
self.datePicker = nil
}
}
}
}
extension ResourceFilterBoxView : UITableViewDataSource,UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: ResourceFilterBoxTableViewCell = tableView.dequeueReusableCell(withIdentifier: "ResourceFilterBoxTableViewCell", for: indexPath) as! ResourceFilterBoxTableViewCell
cell.cellTag = indexPath.row
cell.selectButton.setTitle(tableData[indexPath.row], for: .normal)
cell.callBack = { [weak self] cellTag in
guard let self = self else { return }
self.currentIndex = cellTag as! Int
self.tableView.reloadData()
}
if indexPath.row == currentIndex {
cell.selectButton.layer.borderWidth = 1.0
cell.selectButton.setTitleColor(UIColor(red: 0, green: 0.51, blue: 1, alpha: 1), for: .normal)
} else {
cell.selectButton.layer.borderWidth = 0
cell.selectButton.setTitleColor(UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1), for: .normal)
}
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 56
}
}
//
// YearMonthPickerView.swift
// PhoneManager
//
// Created by edy on 2025/5/12.
//
import Foundation
enum PikerDateType {
case start
case end
}
class YearMonthPickerView: UIView {
var type : PikerDateType = .start
// MARK: - Properties
private var selectedYear: Int = 0
private var selectedMonth: Int = 0
var onCancel: (() -> Void)?
var onConfirm: ((_ type : PikerDateType,_ year: Int, _ month: Int) -> Void)?
private let pickerView: UIPickerView = {
let picker = UIPickerView()
picker.backgroundColor = .white
return picker
}()
private let years = Array(Array(Calendar.current.component(.year, from: Date())-30...Calendar.current.component(.year, from: Date())).reversed())
private lazy var months: [String] = {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US")
return formatter.monthSymbols
}()
// MARK: - Initialization
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
// MARK: - Setup
private func setupView() {
backgroundColor = .white
layer.cornerRadius = 12
layer.masksToBounds = true
// 按钮容器
let buttonContainer = UIView()
buttonContainer.backgroundColor = UIColor(red: 0.9, green: 0.9, blue: 0.9, alpha: 1)
buttonContainer.translatesAutoresizingMaskIntoConstraints = false
// 取消按钮
let cancelButton = UIButton(type: .system)
cancelButton.setTitle("Cancel", for: .normal)
cancelButton.contentHorizontalAlignment = .left
cancelButton.addTarget(self, action: #selector(cancelAction), for: .touchUpInside)
cancelButton.translatesAutoresizingMaskIntoConstraints = false
cancelButton.setTitleColor(UIColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1), for: .normal)
cancelButton.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .medium)
// 确认按钮
let confirmButton = UIButton(type: .system)
confirmButton.setTitle("Completed", for: .normal)
confirmButton.contentHorizontalAlignment = .right
confirmButton.addTarget(self, action: #selector(confirmAction), for: .touchUpInside)
confirmButton.translatesAutoresizingMaskIntoConstraints = false
cancelButton.setTitleColor(UIColor(red: 0.07, green: 0.07, blue: 0.07, alpha: 1), for: .normal)
cancelButton.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .medium)
buttonContainer.addSubview(cancelButton)
buttonContainer.addSubview(confirmButton)
// 按钮容器约束
NSLayoutConstraint.activate([
buttonContainer.heightAnchor.constraint(equalToConstant: 42),
cancelButton.leadingAnchor.constraint(equalTo: buttonContainer.leadingAnchor, constant: 16),
cancelButton.centerYAnchor.constraint(equalTo: buttonContainer.centerYAnchor),
confirmButton.trailingAnchor.constraint(equalTo: buttonContainer.trailingAnchor, constant: -16),
confirmButton.centerYAnchor.constraint(equalTo: buttonContainer.centerYAnchor)
])
// 主布局
let stackView = UIStackView(arrangedSubviews: [buttonContainer, pickerView])
stackView.axis = .vertical
stackView.spacing = 0
stackView.translatesAutoresizingMaskIntoConstraints = false
addSubview(stackView)
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: topAnchor),
stackView.leadingAnchor.constraint(equalTo: leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: trailingAnchor),
stackView.bottomAnchor.constraint(equalTo: bottomAnchor)
])
pickerView.delegate = self
pickerView.dataSource = self
// 设置初始选择
let currentYear = Calendar.current.component(.year, from: Date())
let currentMonth = Calendar.current.component(.month, from: Date())
selectedYear = currentYear
selectedMonth = currentMonth - 1
if let yearIndex = years.firstIndex(of: currentYear) {
pickerView.selectRow(yearIndex, inComponent: 1, animated: false)
}
pickerView.tintColor = .red
pickerView.selectRow(currentMonth - 1, inComponent: 0, animated: false)
}
// MARK: - 按钮操作
@objc private func cancelAction() {
onCancel?()
}
@objc private func confirmAction() {
onConfirm?(self.type,selectedYear, selectedMonth + 1)
}
}
// MARK: - UIPickerView DataSource & Delegate
extension YearMonthPickerView: UIPickerViewDataSource, UIPickerViewDelegate {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 2
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return component == 0 ? months.count : years.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return component == 0 ? months[row] : "\(years.reversed()[row])"
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if component == 0 {
selectedMonth = row
} else {
selectedYear = years.reversed()[row]
}
}
func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat {
return component == 0 ? self.width/2 : self.width/2
}
}
......@@ -20,6 +20,8 @@ class HomeInfoTableViewCell:UITableViewCell {
private var seletedAllBtn:UIButton?
var type : PhotsFileType?
var callBack:callBack<Any> = {text in}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
......@@ -115,10 +117,27 @@ class HomeInfoTableViewCell:UITableViewCell {
UIView.transition(with: collectionView!, duration: 0.3, options: .transitionCrossDissolve, animations: {
self.collectionView?.reloadData()
// self.reloadCollectionView()
}, completion: nil)
}
}
// 重新刷新下集合
func reloadCollectionView(){
DispatchQueue.main.async {
if let collectionView = self.collectionView {
for section in 0..<collectionView.numberOfSections {
for item in 0..<collectionView.numberOfItems(inSection: section) {
UIView.transition(with:collectionView, duration: 0.3, options: .transitionCrossDissolve, animations: {
collectionView.reloadItems(at: [IndexPath(row: item, section: section)])
}, completion: nil)
}
}
}
}
}
@objc func seletedAllBtnClick() {
DispatchQueue.main.async {[weak self] in
......@@ -195,6 +214,11 @@ extension HomeInfoTableViewCell:UICollectionViewDelegate,UICollectionViewDataSou
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ImageSeletedCollectionCell.identifiers, for: indexPath) as! ImageSeletedCollectionCell
// 显示保留按钮或者最佳匹配结果按钮
cell.allKeepButton.isHidden = indexPath.item != 0 || self.type == .duplicates
cell.bestResultButton.isHidden = indexPath.item != 0 || self.type == .duplicates
cell.model = model?.smodels?[indexPath.row]
cell.photsFileType = model?.type
......
......@@ -19,6 +19,34 @@ class ImageSeletedCollectionCell:UICollectionViewCell {
var callBack:callBack<Any> = {text in}
lazy var allKeepButton : UIButton = {
let button = UIButton(type: .custom)
button.backgroundColor = UIColor(red: 0.33, green: 0.77, blue: 0.49, alpha: 1)
button.layer.cornerRadius = 7
button.clipsToBounds = true
button.setTitle( "All retained", for: .normal)
button.setTitleColor(.white, for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 9, weight: .semibold)
button.addTarget(self, action: #selector(allKeepButtonAction), for: .touchUpInside)
button.isHidden = true
return button
}()
lazy var bestResultButton : UIButton = {
let button = UIButton(type: .custom)
button.layer.cornerRadius = 8
button.clipsToBounds = true
button.backgroundColor = UIColor(red: 0, green: 0.51, blue: 1, alpha: 1)
button.setTitle( "Best result", for: .normal)
button.setImage(UIImage(named: "Frame"), for: .normal)
button.setTitleColor(.white, for: .normal)
button.isHidden = true
button.titleLabel?.font = UIFont.systemFont(ofSize: 10, weight: .semibold)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
......@@ -132,9 +160,6 @@ class ImageSeletedCollectionCell:UICollectionViewCell {
self.backgroundColor = .clear
}
func addViews() {
......@@ -142,6 +167,22 @@ class ImageSeletedCollectionCell:UICollectionViewCell {
self.addSubview(backImageView!)
self.addSubview(seletedBtn!)
self.addSubview(self.extensionView)
self.addSubview(self.allKeepButton)
self.addSubview(self.bestResultButton)
self.allKeepButton.snp.makeConstraints { make in
make.top.left.equalToSuperview()
make.width.equalTo(58)
make.height.equalTo(14)
}
self.bestResultButton.snp.makeConstraints { make in
make.left.bottom.equalToSuperview()
make.width.equalTo(81)
make.height.equalTo(16)
}
}
@objc func seletedBtnClick() {
......@@ -178,3 +219,9 @@ class ImageSeletedCollectionCell:UICollectionViewCell {
}
extension ImageSeletedCollectionCell {
@objc func allKeepButtonAction(){
Print("点击了全部保留按钮")
}
}
//
// ResourceFilterBoxTableViewCell.swift
// PhoneManager
//
// Created by edy on 2025/5/12.
//
import Foundation
class ResourceFilterBoxTableViewCell : UITableViewCell {
var cellTag : Int = 0
var callBack : callBack<Any> = {cellTag in}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.selectionStyle = .none
self.contentView.addSubview(self.selectButton)
self.selectButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(0)
make.right.equalToSuperview().offset(0)
make.top.equalToSuperview().offset(7)
make.bottom.equalToSuperview().offset(-7)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
lazy var selectButton : UIButton = {
let view = UIButton(type: UIButton.ButtonType.custom)
view.backgroundColor = UIColor(red: 0.95, green: 0.96, blue: 0.99, alpha: 1)
view.layer.cornerRadius = 12
view.clipsToBounds = true
view.setTitleColor(UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1), for: .normal)
view.addTarget(self, action: #selector(selectAction), for: .touchUpInside)
view.layer.borderColor = UIColor(red: 0, green: 0.51, blue: 1, alpha: 1).cgColor
return view
}()
@objc func selectAction(){
callBack(self.cellTag)
}
}
......@@ -17,30 +17,15 @@ class PMShowImgCell: UICollectionViewCell {
callblock()
}
var isCurrent:Bool = false {
didSet {
selectBtn.isSelected = isCurrent
}
}
var icon:UIImage = UIImage() {
didSet {
scaleImg.icon = icon
// iconView.image = icon
// var size = icon.size
// if size.width != 0 && size.height != 0 {
// if size.height < size.width {
// let width = self.width
// size = CGSize(width: width, height: size.height * (width/size.width) )
// }else{
// let width = self.width
// let height = size.height * (width/size.width)
// if height > self.height {
// size = CGSize(width: width * (self.height/height), height: height )
// }else {
// size = CGSize(width: width, height: height )
// }
// }
// }
//
// iconView.snp.remakeConstraints { make in
// make.centerX.centerY.equalToSuperview()
// make.size.equalTo(size)
// }
}
}
......@@ -54,8 +39,8 @@ class PMShowImgCell: UICollectionViewCell {
lazy var selectBtn: UIButton = {
let select = UIButton(type: .custom)
select.setImage(UIImage(named: ""), for: .normal)
select.setImage(UIImage(named: ""), for: .selected)
select.setImage(UIImage(named: "home_info_norl"), for: .normal)
select.setImage(UIImage(named: "home_info_seleted"), for: .selected)
select.addTarget(self, action: #selector(selectTap), for: .touchUpInside)
contentView.addSubview(select)
return select
......
......@@ -14,13 +14,7 @@ class PMShowItemCell: UICollectionViewCell {
var isCurrent:Bool = false {
didSet {
if isCurrent {
layer.borderColor = UIColor.colorWithHex(hexStr: "#0082FF").cgColor
layer.borderWidth = 4
}else{
layer.borderColor = UIColor.clear.cgColor
layer.borderWidth = 0
}
selectBtn.isSelected = isCurrent
}
}
......@@ -39,8 +33,8 @@ class PMShowItemCell: UICollectionViewCell {
private lazy var selectBtn: UIButton = {
let select = UIButton(type: .custom)
select.setImage(UIImage(named: ""), for: .normal)
select.setImage(UIImage(named: ""), for: .selected)
select.setImage(UIImage(named: "home_info_norl"), for: .normal)
select.setImage(UIImage(named: "home_info_seleted"), for: .selected)
contentView.addSubview(select)
select.isUserInteractionEnabled = false
return select
......
......@@ -137,11 +137,6 @@ extension PMShowImgVideoController : UICollectionViewDelegate,UICollectionViewDa
}
self.MaxCollection.reloadData()
collectionView.reloadData()
// if currentIdx != indexPath.row {
// currentIdx = indexPath.row
// MaxCollection.scrollToItem(at: indexPath, at: .left, animated: false)
// collectionView.reloadData()
// }
}
}
......@@ -177,6 +172,17 @@ extension PMShowImgVideoController : UICollectionViewDelegate,UICollectionViewDa
showName.loadPhotoOrVideo { durs, icon in
cell.icon = icon
}
cell.isCurrent = selectSet.contains(indexPath.row)
cell.callblock = {[weak self] in
guard let self = self else { return }
if self.selectSet.contains(indexPath.row){
self.selectSet.remove(indexPath.row)
}else{
self.selectSet.add(indexPath.row)
}
self.MaxCollection.reloadData()
self.bottItems.reloadData()
}
return cell
}else{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PMShowVideoCellID, for: indexPath) as! PMShowVideoCell
......@@ -191,7 +197,7 @@ extension PMShowImgVideoController : UICollectionViewDelegate,UICollectionViewDa
cell.icon = icon
}
}
cell.isCurrent = (self.currentIdx == indexPath.row)
cell.isCurrent = selectSet.contains(indexPath.row)
return cell
}
}
......@@ -201,7 +207,7 @@ extension PMShowImgVideoController : UICollectionViewDelegate,UICollectionViewDa
if collectionView == MaxCollection {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PMShowImgCellID, for: indexPath) as! PMShowImgCell
cell.icon = self.homeDataSource![indexPath.row].image ?? UIImage()
cell.isSelected = selectSet.contains(indexPath.row)
cell.isCurrent = selectSet.contains(indexPath.row)
cell.callblock = {[weak self] in
guard let self = self else { return }
if self.selectSet.contains(indexPath.row){
......@@ -246,7 +252,7 @@ extension PMShowImgVideoController : UICollectionViewDelegate,UICollectionViewDa
if indexPath.row < self.homeDataSource?.count ?? 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PMShowItemCellID, for: indexPath) as! PMShowItemCell
cell.icon = self.homeDataSource![indexPath.row].image ?? UIImage()
cell.isCurrent = (self.currentIdx == indexPath.row)
cell.isCurrent = selectSet.contains(indexPath.row)
return cell
}
}
......@@ -259,11 +265,18 @@ extension PMShowImgVideoController : UICollectionViewDelegate,UICollectionViewDa
if collectionView == MaxCollection {
return collectionView.size
}
if state == .secret {
if indexPath.row < imageVideoPath.count {
return CGSize(width: 68, height: 68)
}
return CGSize(width: collectionView.width - 68, height: 68)
}else{
if indexPath.row < self.homeDataSource?.count ?? 0 {
return CGSize(width: 68, height: 68)
}
return CGSize(width: collectionView.width - 68, height: 68)
}
}
/// 根据LocalIdentifier获取视频的URL
......
......@@ -60,10 +60,14 @@ func getSettingViewInfo() -> [SettingModel] {
[RowInfoModel(imageName: "ic_more_setting",title: "More Apps From Us")]),
SettingModel(sectionTitle: "UTILITIES",rowInfo:
[RowInfoModel(imageName: "ic_widgets_setting",title: "Widgets")]),
SettingModel(sectionTitle: "OTHERS",rowInfo:
[RowInfoModel(imageName: "ic_list_setting",title: "Keep List")]),
// SettingModel(sectionTitle: "SECRET SPACE",rowInfo: [
// RowInfoModel(imageName: "ic_pin_setting",title: "Use PIN")]),
SettingModel(sectionTitle: "STAY IN TOUCH",rowInfo:
[RowInfoModel(imageName: "ic_rate_setting",title: "Rate App"),
RowInfoModel(imageName: "ic_share_setting",title: "Share App"),
/*RowInfoModel(imageName: "ic_ins_setting",title: "Follow on Instagram")*/])]
/*RowInfoModel(imageName: "ic_ins_setting",title: "Follow on Instagram")*/]),
SettingModel(sectionTitle: "STAY IN TOUCH",rowInfo:
[RowInfoModel(imageName: "ic_email_setting",title: "Email support") ])]
}
......@@ -159,12 +159,35 @@ class SettingViewController : BaseViewController , UITableViewDelegate, UITableV
self.navigationController?.pushViewController(widget, animated: true)
break
case 3:
// 保留列表
break
case 4:
if indexPath.row == 0 { // 评分
self.review()
}else if indexPath.row == 1 { // 分享
self.PhoneShare()
}
break
case 5:
if indexPath.row == 0 { // 邮件登录和退出
if PMEmailManager.shareManager.loginUser != nil {
let alert = EmailContentDelAlert(state: .list)
alert.callblock = { idx in
if idx == 1 {
PMEmailManager.shareManager.signOut { success in
if success {
}
}
}
}
alert.show()
}else{
let vc:EmailLoginController = EmailLoginController()
vc.state = .set
self.navigationController?.pushViewController(vc, animated: true)
}
}
default:
break
}
......
//
// EmailFilterSelectCell.swift
// EmailFilterFAVORITECell.swift
// PhoneManager
//
// Created by edy on 2025/5/9.
// Created by edy on 2025/5/12.
//
import UIKit
class EmailFilterBaseCell: UITableViewCell {
class EmailFilterFAVORITECell: UITableViewCell {
static let id = "EmailFilterFAVORITECell"
@IBOutlet weak var titleL: UILabel!
var callblock:((Bool)->Void) = { select in}
@IBOutlet weak var emSwtich: UISwitch!
var top:Bool = false
var bot:Bool = false
......@@ -28,9 +34,10 @@ class EmailFilterBaseCell: UITableViewCell {
}
}
override func awakeFromNib() {
super.awakeFromNib()
@IBAction func selectActions(_ sender: UISwitch) {
// sender.isOn = !sender.isOn
callblock(sender.isOn)
}
@IBOutlet weak var titleL: UILabel!
}
......@@ -10,20 +10,20 @@
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="KGk-i7-Jjw" customClass="EmailFilterBaseCell">
<rect key="frame" x="0.0" y="0.0" width="320" height="50"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="KGk-i7-Jjw" customClass="EmailFilterFAVORITECell" customModule="PhoneManager" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="320" height="60"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="320" height="50"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="60"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bYm-Ap-ASH">
<rect key="frame" x="16" y="0.0" width="0.0" height="50"/>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Cz5-6m-Mah">
<rect key="frame" x="15" y="0.0" width="236" height="60"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="50" id="ByC-vP-p5G"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="60" id="q5h-W4-mcE"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="14"/>
<color key="textColor" red="0.20000001788139343" green="0.20000001788139343" blue="0.20000001788139343" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="textColor" red="0.20000001789999999" green="0.20000001789999999" blue="0.20000001789999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="borderWidth">
......@@ -31,19 +31,33 @@
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="Z64-4z-Cnp">
<rect key="frame" x="256" y="14.666666666666664" width="51" height="31"/>
<constraints>
<constraint firstAttribute="height" constant="31" id="2yv-Iv-20a"/>
<constraint firstAttribute="width" constant="49" id="YS2-4n-lOj"/>
</constraints>
<connections>
<action selector="selectActions:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="nvW-EK-ui8"/>
</connections>
</switch>
</subviews>
<color key="backgroundColor" red="0.9490196704864502" green="0.96470588445663452" blue="0.98823529481887817" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="bYm-Ap-ASH" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="64l-PN-erS"/>
<constraint firstItem="bYm-Ap-ASH" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="16" id="Phe-hO-ZzN"/>
<constraint firstAttribute="bottom" secondItem="bYm-Ap-ASH" secondAttribute="bottom" id="qz2-bx-e67"/>
<constraint firstItem="Cz5-6m-Mah" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="0Mq-J4-Xeq"/>
<constraint firstItem="Z64-4z-Cnp" firstAttribute="leading" secondItem="Cz5-6m-Mah" secondAttribute="trailing" constant="5" id="4nT-3I-qbv"/>
<constraint firstAttribute="bottom" secondItem="Cz5-6m-Mah" secondAttribute="bottom" id="M27-P5-Ro1"/>
<constraint firstItem="Cz5-6m-Mah" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="15" id="NKG-qu-La9"/>
<constraint firstItem="Z64-4z-Cnp" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="tay-in-p1H"/>
<constraint firstAttribute="trailing" secondItem="Z64-4z-Cnp" secondAttribute="trailing" constant="15" id="ucO-vb-yyW"/>
</constraints>
</tableViewCellContentView>
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
<color key="backgroundColor" red="0.94901960784313721" green="0.96470588235294119" blue="0.9882352941176471" alpha="1" colorSpace="calibratedRGB"/>
<connections>
<outlet property="titleL" destination="bYm-Ap-ASH" id="mmr-1G-Kh5"/>
<outlet property="emSwtich" destination="Z64-4z-Cnp" id="Tqg-qK-SLh"/>
<outlet property="titleL" destination="Cz5-6m-Mah" id="tli-w9-h55"/>
</connections>
<point key="canvasLocation" x="104" y="-11"/>
<point key="canvasLocation" x="131" y="-12"/>
</tableViewCell>
</objects>
</document>
//
// EmailFilterKeywordCell.swift
// PhoneManager
//
// Created by edy on 2025/5/12.
//
import UIKit
class EmailFilterKeywordCell: UITableViewCell {
static let id = "EmailFilterKeywordCell"
@IBOutlet weak var kwTitleL: UILabel!
var callblock:((String,Bool)->Void) = { event, select in}
@IBOutlet weak var emSwitch: UISwitch!
override func awakeFromNib() {
super.awakeFromNib()
}
@IBAction func switchValueChange(_ sender: UISwitch) {
callblock("switch",sender.isOn)
}
var top:Bool = false
var bot:Bool = false
func reload() -> Void {
DispatchQueue.main.async {
if self.top && self.bot {
self.cornerCut(radius: 12, corner: [.allCorners])
}else if self.top {
self.cornerCut(radius: 12, corner: [.topLeft,.topRight])
}else if self.bot {
self.cornerCut(radius: 12, corner: [.bottomLeft,.bottomRight])
}else{
self.cornerCut(radius: 0, corner: [.allCorners])
}
}
}
@IBAction func AddKeyWordActions(_ sender: Any) {
callblock("keyword",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="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"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="144" id="KGk-i7-Jjw" customClass="EmailFilterKeywordCell" customModule="PhoneManager" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="456" height="144"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="456" height="144"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Hop-Ic-sfc">
<rect key="frame" x="0.0" y="0.0" width="456" height="84"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AJp-c0-6Ib">
<rect key="frame" x="15" y="0.0" width="367" height="84"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="72" id="g5j-o3-20E"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="u6a-TR-UVD">
<rect key="frame" x="392" y="26.666666666666671" width="51" height="31"/>
<constraints>
<constraint firstAttribute="height" constant="31" id="55x-6W-qb7"/>
<constraint firstAttribute="width" constant="49" id="XoH-WD-4ln"/>
</constraints>
<connections>
<action selector="switchValueChange:" destination="KGk-i7-Jjw" eventType="valueChanged" id="EMy-ik-HnY"/>
</connections>
</switch>
</subviews>
<color key="backgroundColor" red="0.94901960784313721" green="0.96470588235294119" blue="0.9882352941176471" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="u6a-TR-UVD" firstAttribute="centerY" secondItem="Hop-Ic-sfc" secondAttribute="centerY" id="17f-0M-5Hj"/>
<constraint firstAttribute="trailing" secondItem="u6a-TR-UVD" secondAttribute="trailing" constant="15" id="2d2-f1-5AW"/>
<constraint firstItem="AJp-c0-6Ib" firstAttribute="leading" secondItem="Hop-Ic-sfc" secondAttribute="leading" constant="15" id="OUs-KH-rbZ"/>
<constraint firstAttribute="bottom" secondItem="AJp-c0-6Ib" secondAttribute="bottom" id="i5F-g5-TWS"/>
<constraint firstItem="u6a-TR-UVD" firstAttribute="leading" secondItem="AJp-c0-6Ib" secondAttribute="trailing" constant="10" id="i6F-Ji-r51"/>
<constraint firstItem="AJp-c0-6Ib" firstAttribute="top" secondItem="Hop-Ic-sfc" secondAttribute="top" id="l50-Us-I0B"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="Radius">
<real key="value" value="12"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="AcA-F1-B4u">
<rect key="frame" x="0.0" y="99" width="136" height="45"/>
<color key="backgroundColor" red="0.94901960784313721" green="0.96470588235294119" blue="0.9882352941176471" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="height" constant="45" id="Xtx-DS-XGd"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<inset key="contentEdgeInsets" minX="10" minY="10" maxX="20" maxY="10"/>
<inset key="titleEdgeInsets" minX="5" minY="0.0" maxX="-5" maxY="0.0"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="Add Keyword" image="icon_add">
<color key="titleColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</state>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="Radius">
<real key="value" value="8"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<action selector="AddKeyWordActions:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="Y5I-Cr-UaT"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="Hop-Ic-sfc" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" id="8Bd-4Q-CmS"/>
<constraint firstAttribute="trailing" secondItem="Hop-Ic-sfc" secondAttribute="trailing" id="MvI-YU-exU"/>
<constraint firstAttribute="bottom" secondItem="AcA-F1-B4u" secondAttribute="bottom" id="cKw-Zm-qz0"/>
<constraint firstItem="AcA-F1-B4u" firstAttribute="top" secondItem="Hop-Ic-sfc" secondAttribute="bottom" constant="15" id="hCj-pu-5gm"/>
<constraint firstItem="AcA-F1-B4u" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" id="vUG-fw-GkT"/>
<constraint firstItem="Hop-Ic-sfc" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="vkW-OW-FZ3"/>
</constraints>
</tableViewCellContentView>
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
<connections>
<outlet property="emSwitch" destination="u6a-TR-UVD" id="GEj-db-Vul"/>
<outlet property="kwTitleL" destination="AJp-c0-6Ib" id="kqS-dt-JUz"/>
</connections>
<point key="canvasLocation" x="233.58778625954199" y="23.943661971830988"/>
</tableViewCell>
</objects>
<resources>
<image name="icon_add" width="20" height="20"/>
</resources>
</document>
......@@ -7,23 +7,37 @@
import UIKit
class EmailFilterSelectCell: EmailFilterBaseCell {
class EmailFilterSelectCell: UITableViewCell {
static let id = "EmailFilterSelectCell"
@IBOutlet weak var selectBtn: UIButton!
@IBOutlet weak var titleL: UILabel!
var callblock:((Bool)->Void) = { select in}
var top:Bool = false
var bot:Bool = false
override func awakeFromNib() {
super.awakeFromNib()
self.titleL.snp.remakeConstraints { make in
make.right.equalTo(selectBtn.snp.left).offset(-5)
make.top.bottom.equalToSuperview()
make.left.equalToSuperview().offset(16)
make.height.equalTo(50)
func reload() -> Void {
DispatchQueue.main.async {
if self.top && self.bot {
self.cornerCut(radius: 12, corner: [.allCorners])
}else if self.top {
self.cornerCut(radius: 12, corner: [.topLeft,.topRight])
}else if self.bot {
self.cornerCut(radius: 12, corner: [.bottomLeft,.bottomRight])
}else{
self.cornerCut(radius: 0, corner: [.allCorners])
}
}
}
@IBAction func selectActions(_ sender: UIButton) {
sender.isSelected = !sender.isSelected
if sender.isSelected {
return
}
sender.isSelected = true
callblock(sender.isSelected)
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
//
// EmailKeyWordItemsCell.swift
// PhoneManager
//
// Created by edy on 2025/5/12.
//
import UIKit
class EmailKeyWordItemsCell: UITableViewCell {
static let id = "EmailKeyWordItemsCell"
@IBOutlet weak var EmailFilterKeywordCollection: UICollectionView!
@IBOutlet weak var collectionFlowLayout: EmailKeywordCollectionFlowLayout!
@IBOutlet weak var collectHeightConstraint: NSLayoutConstraint!
var callblock:(()->Void) = {}
var datasoure:[String] = []
override func awakeFromNib() {
super.awakeFromNib()
self.EmailFilterKeywordCollection.delegate = self
self.EmailFilterKeywordCollection.dataSource = self;
self.EmailFilterKeywordCollection.register(EmailKeywordCollectionCell.self, forCellWithReuseIdentifier: EmailKeywordCollectionCell.id)
self.collectionFlowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
}
func reload() -> Void {
self.layoutIfNeeded()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.reloadItems()
}
}
private func reloadItems() -> Void {
self.EmailFilterKeywordCollection.reloadData()
self.layoutIfNeeded()
DispatchQueue.main.asyncAfter(deadline: .now()+0.1, execute: {
Print("fefsfsfsf \(self.EmailFilterKeywordCollection.contentSize.height)")
let height = self.EmailFilterKeywordCollection.height
if self.datasoure.count > 0 {
self.collectHeightConstraint.constant = self.EmailFilterKeywordCollection.contentSize.height
}else{
self.collectHeightConstraint.constant = 0
}
if height != self.EmailFilterKeywordCollection.contentSize.height {
self.callblock()
}
})
}
}
extension EmailKeyWordItemsCell : UICollectionViewDataSource,UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return datasoure.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: EmailKeywordCollectionCell.id, for: indexPath) as! EmailKeywordCollectionCell
cell.titleL.text = datasoure[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
datasoure.remove(at: indexPath.row)
EmailFilterManager.share.keyword = datasoure
reloadItems()
self.callblock()
}
}
<?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="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"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="96" id="KGk-i7-Jjw" customClass="EmailKeyWordItemsCell" customModule="PhoneManager" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="362" height="96"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="362" height="96"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="none" translatesAutoresizingMaskIntoConstraints="NO" id="mE1-4I-UM9">
<rect key="frame" x="0.0" y="0.0" width="362" height="96"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" id="wh6-On-2nF"/>
</constraints>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="nsY-D4-rsQ" customClass="EmailKeywordCollectionFlowLayout" customModule="PhoneManager" customModuleProvider="target">
<size key="itemSize" width="128" height="128"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
</collectionViewFlowLayout>
</collectionView>
</subviews>
<constraints>
<constraint firstItem="mE1-4I-UM9" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="67S-7k-7lw"/>
<constraint firstAttribute="trailing" secondItem="mE1-4I-UM9" secondAttribute="trailing" id="KsC-DY-CKi"/>
<constraint firstAttribute="bottom" secondItem="mE1-4I-UM9" secondAttribute="bottom" id="shZ-1J-L2E"/>
<constraint firstItem="mE1-4I-UM9" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" id="wM7-6Q-v1T"/>
</constraints>
</tableViewCellContentView>
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
<connections>
<outlet property="EmailFilterKeywordCollection" destination="mE1-4I-UM9" id="gLL-fG-BEi"/>
<outlet property="collectHeightConstraint" destination="wh6-On-2nF" id="HHX-uQ-kyq"/>
<outlet property="collectionFlowLayout" destination="nsY-D4-rsQ" id="Mhl-0Z-vY8"/>
</connections>
<point key="canvasLocation" x="161.83206106870227" y="5.6338028169014089"/>
</tableViewCell>
</objects>
</document>
//
// EmailKeywordCollectionCell.swift
// PhoneManager
//
// Created by edy on 2025/5/12.
//
import UIKit
class EmailKeywordCollectionCell: UICollectionViewCell {
static let id = "EmailKeywordCollectionCell"
override init(frame: CGRect) {
super.init(frame: frame)
setuup()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setuup() -> Void {
content.snp.makeConstraints { make in
make.left.bottom.equalToSuperview()
make.top.equalToSuperview().offset(5)
make.right.equalTo(titleL.snp.right).offset(12)
}
titleL.snp.makeConstraints { make in
make.left.right.equalToSuperview().inset(12)
make.top.equalToSuperview().offset(17)
make.bottom.equalToSuperview().inset(10)
}
icon.snp.makeConstraints { make in
make.right.equalToSuperview().offset(3)
make.top.equalToSuperview()
}
DispatchQueue.main.async {
self.content.layer.borderColor = UIColor.colorWithHex(hexStr: "#B3B3B3").cgColor
self.content.layer.borderWidth = 0.5
self.content.layer.cornerRadius = 8
self.content.clipsToBounds = true
}
}
private lazy var content: UIView = {
let c = UIView()
c.backgroundColor = .clear
contentView.addSubview(c)
return c
}()
private lazy var icon: UIImageView = {
let v = UIImageView(image: UIImage(named: "icon_email_close"))
v.contentMode = .scaleAspectFill
contentView.addSubview(v)
return v
}()
lazy var titleL: UILabel = {
let l = UILabel()
contentView.addSubview(l)
l.font = UIFont.boldSystemFont(ofSize: 14)
l.textAlignment = .center
l.textColor = UIColor.colorWithHex(hexStr: "#333333")
return l
}()
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
let attributes = super.preferredLayoutAttributesFitting(layoutAttributes)
let strs:NSString = self.titleL.text as? NSString ?? ""
let frame = strs.boundingRect(with: CGSizeMake(ScreenW-15*2-29.0, .infinity), attributes: [NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 14)], context: nil)
let width = max(40, min(CGRectGetWidth(frame), ScreenW-15*2-29.0)) + 24.0
attributes.frame.size = contentView.systemLayoutSizeFitting(
CGSize(width: width, height: UIView.layoutFittingCompressedSize.height),
withHorizontalFittingPriority: .required,
verticalFittingPriority: .fittingSizeLevel
)
return attributes
}
}
//
// EmailKeywordCollectionFlowLayout.swift
// PhoneManager
//
// Created by edy on 2025/5/12.
//
import UIKit
class EmailKeywordCollectionFlowLayout: UICollectionViewFlowLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
guard let attributes = super.layoutAttributesForElements(in: rect) else { return nil }
var leftMargin = sectionInset.left // 每行起始位置:ml-citation{ref="4,7" data="citationList"}
var currentY = CGFloat(-1)
return attributes.map { attr in
// 换行重置边距:ml-citation{ref="5" data="citationList"}
if attr.frame.origin.y != currentY {
leftMargin = sectionInset.left
currentY = attr.frame.origin.y
}
// 调整 X 坐标实现左对齐:ml-citation{ref="7" data="citationList"}
attr.frame.origin.x = leftMargin
leftMargin += attr.frame.width + minimumInteritemSpacing
return attr
}
}
}
......@@ -13,6 +13,7 @@ class EmailContentDelAlert: UIViewController {
enum EmailStateAlert {
case group
case list
case emailSignOut
}
var callblock:((Int)->Void) = { idx in}
......@@ -26,7 +27,6 @@ class EmailContentDelAlert: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.colorWithHex(hexStr: "#000000", alpha: 0.5)
switch state {
case .group:
EmailContentMessage.text = "Do you want to apply filters\nfirst or delete mails\nimmediately?"
......@@ -38,11 +38,19 @@ class EmailContentDelAlert: UIViewController {
EmailContentAction1.setTitle("Delete", for: .normal)
EmailContentAction2.setTitle("Cancel", for: .normal)
break
case .emailSignOut:
EmailContentMessage.text = "Are you sure you want to quit?"
EmailContentAction1.setTitle("Yes", for: .normal)
EmailContentAction2.setTitle("Cancel", for: .normal)
break
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
func show() -> Void {
self.modalTransitionStyle = .crossDissolve
self.modalPresentationStyle = .overFullScreen
UIViewController.topMostViewController()?.present(self, animated: true)
}
......
//
// EmailSaveController.swift
// PhoneManager
//
// Created by edy on 2025/5/12.
//
import UIKit
class EmailSaveController: UIViewController {
enum SuccessfromState {
case keyword
case signout
}
var state:SuccessfromState = .keyword
override func viewDidLoad() {
super.viewDidLoad()
}
@IBOutlet weak var messageBox: UILabel!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
switch state {
case .keyword:
messageBox.text = "Keyword saved"
messageBox.textColor = UIColor.colorWithHex(hexStr: "#666666")
case .signout:
messageBox.text = "Exit successfully"
messageBox.textColor = .black
}
}
}
<?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="EmailSaveController" customModule="PhoneManager" customModuleProvider="target">
<connections>
<outlet property="messageBox" destination="adM-fJ-AeE" id="CZa-Uh-JGc"/>
<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>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="zXo-fv-YGt">
<rect key="frame" x="109" y="378.66666666666669" width="175" height="95"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="lSd-TZ-jr6">
<rect key="frame" x="38.666666666666657" y="21.333333333333314" width="97.666666666666686" height="52"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_ok_duolicates" translatesAutoresizingMaskIntoConstraints="NO" id="Vuz-Sa-De6">
<rect key="frame" x="31.333333333333343" y="0.0" width="35" height="35"/>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Keyword saved" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="adM-fJ-AeE">
<rect key="frame" x="0.0" y="35" width="97.666666666666671" height="17"/>
<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>
</stackView>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="lSd-TZ-jr6" firstAttribute="centerX" secondItem="zXo-fv-YGt" secondAttribute="centerX" id="asq-Oo-t1z"/>
<constraint firstItem="lSd-TZ-jr6" firstAttribute="centerY" secondItem="zXo-fv-YGt" secondAttribute="centerY" id="gi8-eX-JcK"/>
<constraint firstAttribute="width" constant="175" id="pee-w9-hhU"/>
<constraint firstAttribute="height" constant="95" id="xht-FO-Xc7"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="Radius">
<real key="value" value="20"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</view>
</subviews>
<viewLayoutGuide key="safeArea" id="fnl-2z-Ty3"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="zXo-fv-YGt" firstAttribute="centerY" secondItem="i5M-Pr-FkT" secondAttribute="centerY" id="lHX-qV-grE"/>
<constraint firstItem="zXo-fv-YGt" firstAttribute="centerX" secondItem="i5M-Pr-FkT" secondAttribute="centerX" id="rqA-77-psE"/>
</constraints>
<point key="canvasLocation" x="132" y="-11"/>
</view>
</objects>
<resources>
<image name="ic_ok_duolicates" width="35" height="35"/>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>
......@@ -14,13 +14,16 @@ class EmailCleanController: BaseViewController {
setup()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
PMEmailManager.shareManager.fetchEmails()
}
private func setup() -> Void {
contentView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalTo(titleView.snp.bottom)
}
}
private lazy var contentView: EmailContentView = {
......
......@@ -17,9 +17,17 @@ class EmailFilterController: BaseViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tableView.reloadData()
self.view.layoutIfNeeded()
}
var datasource:[EmailFilterModel] = EmailFilterManager.filter()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
DispatchQueue.main.asyncAfter(deadline: .now()+0.1) {
self.tableView.reloadData()
}
}
var datasource:[EmailFilterModel] = EmailFilterManager.share.filter()
private func setup() -> Void {
titleLabel.snp.makeConstraints({ make in
......@@ -49,13 +57,19 @@ class EmailFilterController: BaseViewController {
}()
private lazy var tableView: UITableView = {
let tab = UITableView(frame: CGRectZero, style: .plain)
let tab = UITableView(frame: CGRectZero, style: .grouped)
tab.delegate = self;
tab.dataSource = self
tab.showsVerticalScrollIndicator = false
tab.showsHorizontalScrollIndicator = false
tab.separatorStyle = .none
tab.backgroundColor = .white
tab.separatorInset = UIEdgeInsets()
tab.sectionHeaderHeight = 50
tab.register(UINib(nibName: EmailFilterSelectCell.id, bundle: nil), forCellReuseIdentifier: EmailFilterSelectCell.id)
tab.register(UINib(nibName: EmailFilterFAVORITECell.id, bundle: nil), forCellReuseIdentifier: EmailFilterFAVORITECell.id)
tab.register(UINib(nibName: EmailFilterKeywordCell.id, bundle: nil), forCellReuseIdentifier: EmailFilterKeywordCell.id)
tab.register(UINib(nibName: EmailKeyWordItemsCell.id, bundle: nil), forCellReuseIdentifier: EmailKeyWordItemsCell.id)
if #available(iOS 15.0, *) {
tab.sectionHeaderTopPadding = 0
}
......@@ -71,10 +85,48 @@ class EmailFilterController: BaseViewController {
emailLogin.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
emailLogin.layer.cornerRadius = 46/2.0
emailLogin.clipsToBounds = true
emailLogin.backgroundColor = UIColor.colorWithHex(hexStr: "#0082FF")
emailLogin.addTarget(self, action: #selector(ApplyAction), for: .touchUpInside)
view.addSubview(emailLogin)
return emailLogin
}()
private func timeSet(_ indexPath:IndexPath) -> Void {
var btnts = self.datasource[indexPath.section].child ?? []
var modify:[EmailFilterChildModel] = []
for (row,var objc) in btnts.enumerated() {
if row == indexPath.row {
objc.isSelect = true
}else{
objc.isSelect = false
}
modify.append(objc)
}
self.datasource[indexPath.section].child = modify
}
private func readUnread(_ indexPath:IndexPath ,isselect:Bool) -> Void {
var btnts = self.datasource[indexPath.section].child ?? []
if isselect {
self.datasource[indexPath.section].child?[indexPath.row].isSelect = isselect
}else{
self.datasource[indexPath.section].child?[indexPath.row].isSelect = isselect
if indexPath.row == 0 {
guard var vab = (self.datasource[indexPath.section].child?[1]) else { return }
if vab.isSelect == false {
vab.isSelect = true
self.datasource[indexPath.section].child?[1] = vab
}
}else{
guard var vab = (self.datasource[indexPath.section].child?[0]) else { return }
if vab.isSelect == false {
vab.isSelect = true
self.datasource[indexPath.section].child?[0] = vab
}
}
}
}
}
extension EmailFilterController : UITableViewDelegate,UITableViewDataSource {
......@@ -87,38 +139,119 @@ extension EmailFilterController : UITableViewDelegate,UITableViewDataSource {
try data.write(to: path)
}catch{
}
self.navigationController?.popViewController(animated: true)
}
func numberOfSections(in tableView: UITableView) -> Int {
return datasource.count
return datasource.count + 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section < datasource.count {
return datasource[section].child?.count ?? 0
}
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: EmailFilterSelectCell.id, for: indexPath) as! EmailFilterSelectCell
if indexPath.section < datasource.count {
let dataA = datasource[indexPath.section].child
let data = dataA?[indexPath.row]
if datasource[indexPath.section].header == "TIME" {
let cell = tableView.dequeueReusableCell(withIdentifier: EmailFilterSelectCell.id, for: indexPath) as! EmailFilterSelectCell
cell.titleL.text = data?.title ?? ""
cell.selectBtn.isSelected = data?.isSelect ?? false
cell.top = (indexPath.row == 0)
cell.bot = (indexPath.row == (dataA?.count ?? 0 - 1))
cell.callblock = {[weak self] select in
guard let self = self else { return }
self.timeSet(indexPath)
tableView.reloadData()
}
cell.selectionStyle = .none
cell.reload()
return cell
}else if datasource[indexPath.section].header == "FAVORITE" ||
datasource[indexPath.section].header == "READ & UNREAD" {
let cell = tableView.dequeueReusableCell(withIdentifier: EmailFilterFAVORITECell.id, for: indexPath) as! EmailFilterFAVORITECell
cell.titleL.text = data?.title ?? ""
cell.emSwtich.isOn = data?.isSelect ?? false
cell.top = (indexPath.row == 0)
cell.selectionStyle = .none
cell.bot = (indexPath.row == (dataA?.count ?? 0 - 1))
cell.callblock = {[weak self] select in
guard let self = self else { return }
if self.datasource[indexPath.section].header == "FAVORITE" {
self.datasource[indexPath.section].child?[indexPath.row].isSelect = select
}else{
self.readUnread(indexPath,isselect: select)
}
tableView.reloadData()
}
cell.reload()
return cell
}else{
let cell = tableView.dequeueReusableCell(withIdentifier: EmailFilterKeywordCell.id, for: indexPath) as! EmailFilterKeywordCell
cell.kwTitleL.text = data?.title ?? ""
cell.emSwitch.isOn = data?.isSelect ?? false
cell.top = (indexPath.row == 0)
cell.selectionStyle = .none
cell.bot = (indexPath.row == (dataA?.count ?? 0 - 1))
cell.callblock = {[weak self] event, select in
guard let self = self else { return }
if event == "switch" {
self.datasource[indexPath.section].child?[indexPath.row].isSelect = select
tableView.reloadData()
}else{
let keyword = EmailKeywordController()
keyword.modalPresentationStyle = .overFullScreen
keyword.callblock = { text in
var keywords = EmailFilterManager.share.keyword
keywords.append(text)
EmailFilterManager.share.keyword = keywords
tableView.reloadData()
}
self.present(keyword, animated: true)
}
}
cell.reload()
return cell
}
}else{
let cell = tableView.dequeueReusableCell(withIdentifier: EmailKeyWordItemsCell.id, for: indexPath) as! EmailKeyWordItemsCell
cell.selectionStyle = .none
cell.datasoure = EmailFilterManager.share.keyword
cell.callblock = {
tableView.reloadData()
}
cell.reload()
return cell
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section < datasource.count {
let header = UILabel()
header.backgroundColor = .white
header.font = UIFont.boldSystemFont(ofSize: 12)
header.text = datasource[section].header ?? ""
header.textColor = UIColor.colorWithHex(hexStr: "#B3B3B3")
return header
}
return UIView()
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if section < datasource.count {
return 50
}
return 0.01
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let v = UIView()
v.backgroundColor = .white
return v
}
}
//
// EmailKeywordController.swift
// PhoneManager
//
// Created by edy on 2025/5/12.
//
import UIKit
class EmailKeywordController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .clear
self.view.addSubview(blur)
setup()
NotificationCenter.default.addObserver(self, selector: #selector(keyBoardShow(_:)), name: UIApplication.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyBoardHide(_:)), name: UIApplication.keyboardWillHideNotification, object: nil)
}
var callblock:((String)->Void) = { keyword in}
@objc func keyBoardShow(_ notication:Notification) -> Void {
guard let info = notication.userInfo,
let duration = info[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval,
let keyboardFrame = info[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else {
return
}
UIView.animate(withDuration: duration) {
self.content.snp.updateConstraints { make in
make.bottom.equalToSuperview().offset(-CGRectGetHeight(keyboardFrame))
}
self.view.layoutIfNeeded()
}
}
@objc func keyBoardHide(_ notication:Notification) -> Void {
guard let info = notication.userInfo,
let duration = info[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval,
let keyboardFrame = info[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else {
return
}
UIView.animate(withDuration: duration) {
self.content.snp.updateConstraints { make in
make.bottom.equalToSuperview()
}
self.view.layoutIfNeeded()
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
DispatchQueue.main.async {
self.blur.frame = self.view.bounds
self.textInput.becomeFirstResponder()
self.content.cornerCut(radius: 16, corner: [.topLeft,.topRight])
}
}
private lazy var blur: UIVisualEffectView = {
let blurEffect = UIBlurEffect(style: .systemChromeMaterialDark)
let blurView = UIVisualEffectView(effect: blurEffect)
blurView.alpha = 0.8
return blurView
}()
private func setup() -> Void {
content.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
}
tl.snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.top.equalToSuperview().offset(12)
make.height.equalTo(20)
}
textInput.snp.makeConstraints { make in
make.left.right.equalToSuperview().inset(10)
make.top.equalTo(tl.snp.bottom).offset(12)
make.height.equalTo(54)
}
saveBtn.snp.makeConstraints { make in
make.left.right.equalTo(textInput)
make.top.equalTo(textInput.snp.bottom).offset(12)
make.height.equalTo(54)
}
cancelBtn.snp.makeConstraints { make in
make.left.right.equalTo(textInput)
make.top.equalTo(saveBtn.snp.bottom).offset(12)
make.height.equalTo(54)
make.bottom.equalToSuperview().offset(-10)
}
self.view.layoutIfNeeded()
}
@objc func emActions(_ sender:UIButton) {
view.endEditing(true)
guard let text:String = self.textInput.text else { return }
if sender.tag == 10 {
let save = EmailSaveController()
save.modalTransitionStyle = .crossDissolve
save.modalPresentationStyle = .overFullScreen
self.present(save, animated: true)
DispatchQueue.main.asyncAfter(deadline: .now()+1.5) {
save.dismiss(animated: true) {
self.callblock(text)
self.dismiss(animated: true)
}
}
}else{
self.dismiss(animated: true)
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
view.endEditing(true)
}
private lazy var tl: UILabel = {
let l = UILabel()
l.text = "Add Keyvrord"
l.font = UIFont.boldSystemFont(ofSize: 14)
l.textAlignment = .center
l.textColor = UIColor.colorWithHex(hexStr: "#B3B3B3")
content.addSubview(l)
return l
}()
private lazy var textInput: PMTextField = {
let t = PMTextField()
t.contentInsets = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15)
t.placeholder = "Keyvrord"
t.backgroundColor = .white
t.Radius = 8
content.addSubview(t)
return t
}()
private lazy var saveBtn: UIButton = {
let save = UIButton(type: .custom)
save.backgroundColor = UIColor.colorWithHex(hexStr: "#0082FF")
save.Radius = 8
save.setTitle("Save", for: .normal)
save.setTitleColor(.white, for: .normal)
save.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
save.tag = 10;
save.addTarget(self, action: #selector(emActions(_:)), for: .touchUpInside)
content.addSubview(save)
return save
}()
private lazy var cancelBtn: UIButton = {
let save = UIButton(type: .custom)
save.backgroundColor = .clear
save.Radius = 8
save.tag = 11;
save.setTitle("Cancel", for: .normal)
save.setTitleColor(UIColor.colorWithHex(hexStr: "#666666"), for: .normal)
save.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
save.addTarget(self, action: #selector(emActions(_:)), for: .touchUpInside)
content.addSubview(save)
return save
}()
private lazy var content: UIView = {
let c = UIView()
c.backgroundColor = UIColor.colorWithHex(hexStr: "#F2F6FC")
view.addSubview(c)
return c
}()
deinit {
NotificationCenter.default.removeObserver(self)
}
}
......@@ -9,6 +9,13 @@ import UIKit
class EmailLoginController: BaseViewController {
enum fromState {
case home
case set
}
var state:fromState = .home
override func viewDidLoad() {
super.viewDidLoad()
setup()
......@@ -40,12 +47,20 @@ class EmailLoginController: BaseViewController {
view.addSubview(empty)
empty.callblock = {[weak self] in
guard let self = self else { return }
PMEmailManager.shareManager.initOAuth(self) { success in
if success {
if self.state == .home {
let gourp = EmailCleanController()
var navigation = self.navigationController?.viewControllers
if navigation?.count ?? 0 > 1 {
navigation![navigation!.count-1] = gourp
self.navigationController?.setViewControllers(navigation ?? [], animated: true)
}
}else{
self.navigationController?.popViewController(animated: true)
}
}
}
}
return empty
}()
......
......@@ -9,9 +9,33 @@ import UIKit
class EmailFilterManager: NSObject {
class func filter() -> [EmailFilterModel] {
static let share = EmailFilterManager()
var keyword:[String] {
set {
guard let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let path2 = path.appendingPathComponent("keyword.json")
do{
let data = try JSONEncoder().encode(newValue)
try data.write(to: path2)
}catch{}
}
get {
guard let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return [] }
let path2 = path.appendingPathComponent("keyword.json")
do {
let dataFile = try Data(contentsOf: path2)
let decode = try JSONDecoder().decode([String].self, from:dataFile)
return decode
}catch{
return []
}
}
}
func filter() -> [EmailFilterModel] {
let data = [ ["header":"TIME","child":[ ["title":"Delete all mails from selected categories",
"isSelect":false],
"isSelect":true],
["title":"Delete 1 week and older mails",
"isSelect":false],
["title":"Delete 1 month and older mails",
......@@ -23,7 +47,7 @@ class EmailFilterManager: NSObject {
"isSelect":false] ]
],
["header":"READ & UNREAD","child":[ ["title":"Delete read mails",
"isSelect":false],
"isSelect":true],
["title":"Delete unread mails",
"isSelect":false]]
],
......@@ -33,11 +57,10 @@ class EmailFilterManager: NSObject {
]
]
guard var path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return [] }
path = path.appendingPathComponent("EmailFilter.json")
guard let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return [] }
let path1 = path.appendingPathComponent("EmailFilter.json")
do {
let dataFile = try Data(contentsOf: path)
let dataFile = try Data(contentsOf: path1)
let decode = try JSONDecoder().decode([EmailFilterModel].self, from:dataFile)
return decode
}catch{
......@@ -63,5 +86,5 @@ struct EmailFilterModel: Codable {
struct EmailFilterChildModel:Codable {
var title:String?
var isSelect:Bool = false
var keyword:[String]?
}
//
// PMTextField.swift
// PhoneManager
//
// Created by edy on 2025/5/12.
//
import UIKit
class PMTextField: UITextField {
var contentInsets = UIEdgeInsets()
override func borderRect(forBounds bounds: CGRect) -> CGRect {
let rect = super.borderRect(forBounds: bounds)
return math(rect)
}
override func textRect(forBounds bounds: CGRect) -> CGRect {
let rect = super.textRect(forBounds: bounds)
return math(rect)
}
override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
let rect = super.placeholderRect(forBounds: bounds)
return CGRect(
x: rect.origin.x,
y: (bounds.height - rect.height) / 2,
width: rect.width,
height: rect.height
)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
let rect = super.editingRect(forBounds: bounds)
return math(rect)
}
private func math(_ bounds:CGRect) -> CGRect {
var rect = bounds
rect.origin.x = contentInsets.left
rect.origin.y = contentInsets.top
rect.size.width = rect.size.width - contentInsets.left - contentInsets.right
rect.size.height = rect.size.height - contentInsets.top - contentInsets.bottom
return rect
}
}
......@@ -23,12 +23,17 @@ class PMScaleImageView: UIView , UIScrollViewDelegate {
didSet {
showImg.image = icon
DispatchQueue.main.asyncAfter(deadline: .now()+0.1, execute: {
let size = self.icon?.size ?? CGSize()
// self.showImg.snp.remakeConstraints({ make in
// make.size.equalTo(size)
// })
// self.layoutIfNeeded()
// self.showImg.center = CGPoint(x: self.scroll.width/2.0, y: self.scroll.height/2.0)
self.layoutIfNeeded()
var size = self.icon?.size ?? CGSize()
if size.width > self.width {
size.width = self.width
size.height = size.height * (self.width / (self.icon?.size.width ?? 1))
}
self.showImg.snp.remakeConstraints({ make in
make.left.equalToSuperview().offset((self.width - size.width)/2.0 )
make.top.equalToSuperview().offset((self.height - size.height)/2.0 )
make.size.equalTo(size)
})
})
}
}
......@@ -37,15 +42,11 @@ class PMScaleImageView: UIView , UIScrollViewDelegate {
scroll.snp.makeConstraints { make in
make.left.right.bottom.top.equalToSuperview()
}
showImg.snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.width.height.equalToSuperview()
}
}
private lazy var showImg: UIImageView = {
let iv = UIImageView()
iv.contentMode = .center
iv.contentMode = .scaleAspectFill
scroll.addSubview(iv)
return iv
}()
......@@ -56,7 +57,7 @@ class PMScaleImageView: UIView , UIScrollViewDelegate {
scroll.showsVerticalScrollIndicator = false
scroll.showsHorizontalScrollIndicator = false
scroll.minimumZoomScale = 1.0
scroll.maximumZoomScale = 3.0
scroll.maximumZoomScale = 4.0
addSubview(scroll)
return scroll
}()
......
//
// PMEmailManager.swift
// PhoneManager
//
// Created by edy on 2025/5/12.
//
import UIKit
import GoogleSignIn
import GoogleAPIClientForREST
class PMEmailManager: NSObject {
static let cloud_id = "797378266354-no81qsffrlpj6p6i1umh981ne2mgtiua.apps.googleusercontent.com"
static let shareManager = PMEmailManager()
let service = GTMSessionFetcherService()
var loginUser:GIDGoogleUser?
func restore() -> Void {
GIDSignIn.sharedInstance.restorePreviousSignIn { user, error in
if error != nil || user == nil {
print("未找到已登录用户")
}
self.loginUser = user
}
}
func signOut(_ complate:@escaping((Bool)->Void)) -> Void {
GIDSignIn.sharedInstance.signOut()
GIDSignIn.sharedInstance.disconnect { error in
if error == nil {
complate(true)
}else{
complate(false)
}
}
}
func initOAuth(_ observer:UIViewController, _ complate:@escaping((Bool)->Void)) -> Void {
GIDSignIn.sharedInstance.configuration = GIDConfiguration(
clientID: PMEmailManager.cloud_id
)
let scopes = [kGTLRAuthScopeGmailModify]
GIDSignIn.sharedInstance.signIn(
withPresenting: observer,
hint: nil,
additionalScopes: scopes
) { result, error in
guard error == nil else { return }
guard let user = result?.user else {
print("登录失败: \(error?.localizedDescription ?? "")")
complate(false)
return
}
self.loginUser = user
complate(true)
}
}
func fetchEmails() {
let service = GTLRGmailService()
service.authorizer = self.loginUser?.fetcherAuthorizer
let query = GTLRGmailQuery_UsersMessagesList.query(withUserId: self.loginUser?.userID ?? "me")
query.labelIds = ["CATEGORY_SOCIAL","CATEGORY_PROMOTIONS","CATEGORY_UPDATES","CATEGORY_FORUMS","SPAM"]
query.maxResults = 50
service.executeQuery(query) { ticket, result, error in
guard let list = result as? GTLRGmail_ListMessagesResponse else { return }
list.messages?.forEach { message in
self.fetchEmailDetails(message.identifier ?? "")
}
}
}
func fetchEmailDetails(_ messageID:String) {
let service = GTLRGmailService()
service.authorizer = self.loginUser?.fetcherAuthorizer
let query = GTLRGmailQuery_UsersMessagesGet.query(
withUserId: self.loginUser?.userID ?? "",
identifier: messageID // 替换具体邮件ID
)
query.format = kGTLRGmailFormatFull // 获取完整邮件内容:ml-citation{ref="1,7" data="citationList"}
service.executeQuery(query) { _, result, error in
guard let message = result as? GTLRGmail_Message else { return }
self.parseMessageDetails(message: message)
}
}
func parseMessageDetails(message: GTLRGmail_Message) {
parseHeaders(message.payload?.headers ?? []) // 解析头信息
if let body = message.payload?.body?.data {
print("正文内容: \(decodeBody(body))")
}
if let parts = message.payload?.parts {
// extractAttachments(parts) // 处理附件
}
}
func decodeBody(_ data: String?) -> String {
guard let encodedData = data?.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/"),
let decodedData = Data(base64Encoded: encodedData, options: .ignoreUnknownCharacters)
else { return "" }
return String(data: decodedData, encoding: .utf8) ?? ""
}
func parseHeaders(_ headers: [GTLRGmail_MessagePartHeader]) {
var from = ""
var subject = ""
headers.forEach { header in
switch header.name?.lowercased() {
case "from": from = header.value ?? ""
case "subject": subject = header.value ?? ""
default: break
}
}
print("发件人: \(from)\n主题: \(subject)")
}
// func extractAttachments(_ parts: [GTLRGmail_MessagePart]) {
// parts.forEach { part in
// guard let filename = part.filename, !filename.isEmpty,
// let body = part.body,
// let attachmentId = body.attachmentId
// else { return }
// let query = GTLRGmailQuery_UsersMessagesAttachmentsGet.query(
// withUserId: "me",
// messageId: "MESSAGE_ID",
// identifier: attachmentId
// )
// service.executeQuery(query) { _, result, _ in
// guard let attachment = result as? GTLRGmail_MessagePartBody else { return }
// let fileData = Data(base64Encoded: attachment.data ?? "")
// }
// }
// }
}
......@@ -8,6 +8,7 @@
import UIKit
import StoreKit
var blurKey = "blurKey"
extension UIView {
......@@ -179,8 +180,46 @@ extension UIView {
// 将渐变图层添加到视图的最底层
self.layer.insertSublayer(gradientLayer, at: 0)
}
var blur:UIVisualEffectView {
set {
objc_setAssociatedObject(self, &blurKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
get {
var blurs = objc_getAssociatedObject(self, &blurKey) as? UIVisualEffectView
if blurs == nil {
let blurEffect = UIBlurEffect(style: .systemUltraThinMaterialDark)
blurs = UIVisualEffectView(effect: blurEffect)
self.blur = blurs!
}
return blurs!
}
}
func showBlur() -> Void {
self.addSubview(blur)
var frame = self.bounds
frame.origin.y = frame.height
blur.frame = frame
UIView.animate(withDuration: 0.1) {
frame.origin.y = 0
self.blur.frame = frame
}
}
func hideBlur() -> Void {
var frame = self.blur.frame
frame.origin.y = frame.height
UIView.animate(withDuration: 0.1) {
self.blur.frame = frame
} completion: { su in
self.blur.removeFromSuperview()
}
}
/// 视图最右边的x坐标(origin.x + width)
var rightX: CGFloat {
return frame.maxX
......
......@@ -2,8 +2,27 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>GIDClientID</key>
<string>797378266354-no81qsffrlpj6p6i1umh981ne2mgtiua.apps.googleusercontent.com</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>com.googleusercontent.apps.797378266354-no81qsffrlpj6p6i1umh981ne2mgtiua</string>
</array>
</dict>
</array>
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-3940256099942544~1458002511</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>google</string>
<string>googlemail</string>
<string>googlegmail</string>
</array>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
......
......@@ -16,6 +16,9 @@ target 'PhoneManager' do
pod 'OpenCV'
pod 'SVProgressHUD'
pod 'Google-Mobile-Ads-SDK'
pod 'GoogleSignIn'
pod 'GoogleAPIClientForREST/Gmail'
post_install do |installer|
installer.pods_project.targets.each do |target|
......
......@@ -11,10 +11,9 @@ import SwiftUI
@main
struct widgetBundle: WidgetBundle {
var body: some Widget {
widgetLockSreenView()
// BatteryWidget()
// BatteryWidget1()
// BatteryWidget2()
BatteryWidget()
BatteryWidget1()
BatteryWidget2()
}
}
......
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