Commit a4c9fd96 authored by yqz's avatar yqz

邮件界面

谷歌邮件登录
锁屏小组件
parent f78b7dc5
{
"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>
This diff is collapsed.
......@@ -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()
return true
}
......@@ -335,7 +337,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}
func application(
_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool {
return GIDSignIn.sharedInstance.handle(url)
}
}
{
"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
}
}
//
// 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]?
}
......@@ -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{
......
......@@ -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
......
......@@ -65,5 +65,7 @@ func getSettingViewInfo() -> [SettingModel] {
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") ])]
}
......@@ -165,6 +165,26 @@ class SettingViewController : BaseViewController , UITableViewDelegate, UITableV
self.PhoneShare()
}
break
case 4:
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
}
......
//
// 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 {
......@@ -180,6 +181,43 @@ 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()
}
}
}
......
......@@ -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