动效
This commit is contained in:
commit
423ff363f6
0
.gitignore
vendored
Normal file
0
.gitignore
vendored
Normal file
375
wake.xcodeproj/project.pbxproj
Normal file
375
wake.xcodeproj/project.pbxproj
Normal file
@ -0,0 +1,375 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 77;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
AB8773632E4E04400071CB53 /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = AB8773622E4E040E0071CB53 /* .gitignore */; };
|
||||
ABB4E2182E4B761400660198 /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = ABB4E2172E4B761400660198 /* Alamofire */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
ABB4E21C2E4B763200660198 /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
AB8773622E4E040E0071CB53 /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = "<group>"; };
|
||||
ABB4E2082E4B75D900660198 /* wake.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = wake.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
ABB4E20A2E4B75D900660198 /* wake */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
path = wake;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
ABB4E2052E4B75D900660198 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
ABB4E2182E4B761400660198 /* Alamofire in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
ABB4E1FF2E4B75D900660198 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AB8773622E4E040E0071CB53 /* .gitignore */,
|
||||
ABB4E20A2E4B75D900660198 /* wake */,
|
||||
ABB4E2092E4B75D900660198 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
ABB4E2092E4B75D900660198 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
ABB4E2082E4B75D900660198 /* wake.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
ABB4E2072E4B75D900660198 /* wake */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = ABB4E2132E4B75DF00660198 /* Build configuration list for PBXNativeTarget "wake" */;
|
||||
buildPhases = (
|
||||
ABB4E2042E4B75D900660198 /* Sources */,
|
||||
ABB4E2052E4B75D900660198 /* Frameworks */,
|
||||
ABB4E2062E4B75D900660198 /* Resources */,
|
||||
ABB4E21C2E4B763200660198 /* Embed Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
ABB4E20A2E4B75D900660198 /* wake */,
|
||||
);
|
||||
name = wake;
|
||||
packageProductDependencies = (
|
||||
ABB4E2172E4B761400660198 /* Alamofire */,
|
||||
);
|
||||
productName = wake;
|
||||
productReference = ABB4E2082E4B75D900660198 /* wake.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
ABB4E2002E4B75D900660198 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = 1;
|
||||
LastSwiftUpdateCheck = 1640;
|
||||
LastUpgradeCheck = 1640;
|
||||
TargetAttributes = {
|
||||
ABB4E2072E4B75D900660198 = {
|
||||
CreatedOnToolsVersion = 16.4;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = ABB4E2032E4B75D900660198 /* Build configuration list for PBXProject "wake" */;
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = ABB4E1FF2E4B75D900660198;
|
||||
minimizedProjectReferenceProxies = 1;
|
||||
packageReferences = (
|
||||
ABB4E2162E4B761400660198 /* XCRemoteSwiftPackageReference "Alamofire" */,
|
||||
);
|
||||
preferredProjectObjectVersion = 77;
|
||||
productRefGroup = ABB4E2092E4B75D900660198 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
ABB4E2072E4B75D900660198 /* wake */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
ABB4E2062E4B75D900660198 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
AB8773632E4E04400071CB53 /* .gitignore in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
ABB4E2042E4B75D900660198 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
ABB4E2112E4B75DF00660198 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = GB3VPJ54BD;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.5;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
ABB4E2122E4B75DF00660198 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = GB3VPJ54BD;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.5;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
ABB4E2142E4B75DF00660198 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = GB3VPJ54BD;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.memowake.wake;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
ABB4E2152E4B75DF00660198 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = GB3VPJ54BD;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.memowake.wake;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
ABB4E2032E4B75D900660198 /* Build configuration list for PBXProject "wake" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
ABB4E2112E4B75DF00660198 /* Debug */,
|
||||
ABB4E2122E4B75DF00660198 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
ABB4E2132E4B75DF00660198 /* Build configuration list for PBXNativeTarget "wake" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
ABB4E2142E4B75DF00660198 /* Debug */,
|
||||
ABB4E2152E4B75DF00660198 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
ABB4E2162E4B761400660198 /* XCRemoteSwiftPackageReference "Alamofire" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/Alamofire/Alamofire.git";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 5.10.2;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
ABB4E2172E4B761400660198 /* Alamofire */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = ABB4E2162E4B761400660198 /* XCRemoteSwiftPackageReference "Alamofire" */;
|
||||
productName = Alamofire;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
rootObject = ABB4E2002E4B75D900660198 /* Project object */;
|
||||
}
|
||||
7
wake.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
wake.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@ -0,0 +1,15 @@
|
||||
{
|
||||
"originHash" : "e8f130fe30ac6cdc940ef06ee1e8535e9f46ffee6aeead1722b9525562f6ce08",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "alamofire",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/Alamofire/Alamofire.git",
|
||||
"state" : {
|
||||
"revision" : "513364f870f6bfc468f9d2ff0a95caccc10044c5",
|
||||
"version" : "5.10.2"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 3
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
<?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">
|
||||
<array/>
|
||||
</plist>
|
||||
BIN
wake.xcodeproj/project.xcworkspace/xcuserdata/elliwood.xcuserdatad/UserInterfaceState.xcuserstate
generated
Normal file
BIN
wake.xcodeproj/project.xcworkspace/xcuserdata/elliwood.xcuserdatad/UserInterfaceState.xcuserstate
generated
Normal file
Binary file not shown.
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Bucket
|
||||
uuid = "A774AEAB-F2DE-4CA6-8FAA-A05AB418F685"
|
||||
type = "1"
|
||||
version = "2.0">
|
||||
</Bucket>
|
||||
@ -0,0 +1,14 @@
|
||||
<?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>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>wake.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
wake/.DS_Store
vendored
Normal file
BIN
wake/.DS_Store
vendored
Normal file
Binary file not shown.
228
wake/ContentView.swift
Normal file
228
wake/ContentView.swift
Normal file
@ -0,0 +1,228 @@
|
||||
import SwiftUI
|
||||
|
||||
// 自定义从左向右的过渡动画
|
||||
extension AnyTransition {
|
||||
static var slideFromLeading: AnyTransition {
|
||||
.asymmetric(
|
||||
insertion: .move(edge: .trailing).combined(with: .opacity),
|
||||
removal: .move(edge: .leading).combined(with: .opacity)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 1. 定义路由
|
||||
enum Route: Hashable {
|
||||
case settings
|
||||
}
|
||||
|
||||
struct ContentView: View {
|
||||
@State private var showModal = false
|
||||
@State private var showSettings = false
|
||||
@State private var navigationPath = NavigationPath()
|
||||
@State private var contentOffset: CGFloat = 0
|
||||
|
||||
var body: some View {
|
||||
NavigationStack(path: $navigationPath) {
|
||||
// 添加动画修饰符到 NavigationStack
|
||||
let _ = Self._printChanges()
|
||||
let _ = print("Navigation path changed: \(navigationPath)")
|
||||
|
||||
ZStack {
|
||||
VStack {
|
||||
VStack(spacing: 20) {
|
||||
// This spacer ensures content stays below the status bar
|
||||
Spacer().frame(height: UIApplication.shared.windows.first?.safeAreaInsets.top ?? 0)
|
||||
// 顶部栏
|
||||
HStack {
|
||||
Spacer()
|
||||
Button(action: {
|
||||
withAnimation(.spring(response: 0.5, dampingFraction: 0.8)) {
|
||||
showModal = true
|
||||
}
|
||||
}) {
|
||||
Image(systemName: "gearshape")
|
||||
.font(.title2)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.background(Color(.systemBackground))
|
||||
.offset(x: showModal ? UIScreen.main.bounds.width * 0.35 : 0)
|
||||
.animation(.spring(response: 0.5, dampingFraction: 0.8), value: showModal)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
}
|
||||
// 添加半透明遮罩层
|
||||
if showModal {
|
||||
Color.black.opacity(0.4)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
.onTapGesture {
|
||||
withAnimation(.spring(response: 0.5, dampingFraction: 0.8)) {
|
||||
showModal = false
|
||||
}
|
||||
}
|
||||
.transition(.opacity)
|
||||
}
|
||||
|
||||
// Modal with animation - will be pushed off-screen by SettingsView
|
||||
SlideInModal(isPresented: $showModal, onDismiss: {
|
||||
withAnimation(.spring(response: 0.5, dampingFraction: 0.8)) {
|
||||
showModal = false
|
||||
}
|
||||
}) {
|
||||
// Modal content
|
||||
// Modal content with offset for SettingsView
|
||||
VStack(spacing: 20) {
|
||||
// 用户信息区域
|
||||
HStack(alignment: .center, spacing: 16) {
|
||||
// 头像
|
||||
Image(systemName: "person.circle.fill")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
.frame(width: 60, height: 60)
|
||||
.foregroundColor(.blue)
|
||||
.clipShape(Circle())
|
||||
|
||||
// 姓名和ID
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text("用户名")
|
||||
.font(.headline)
|
||||
.foregroundColor(.primary)
|
||||
|
||||
Text("ID: 12345678")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.top, 16)
|
||||
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text("会员等级")
|
||||
.font(.headline)
|
||||
.foregroundColor(.primary)
|
||||
Text("会员时间")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
Text("会员中心")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(16)
|
||||
.background(Color(red: 0.92, green: 0.92, blue: 0.92))
|
||||
.cornerRadius(10)
|
||||
.padding(.horizontal, 16)
|
||||
|
||||
VStack(spacing: 12) {
|
||||
// memories
|
||||
Button(action: {
|
||||
print("memories")
|
||||
}) {
|
||||
HStack(spacing: 16) {
|
||||
Image(systemName: "crown.fill")
|
||||
.foregroundColor(.orange)
|
||||
.frame(width: 24, height: 24)
|
||||
|
||||
Text("My Memories")
|
||||
.font(.headline)
|
||||
.foregroundColor(.primary)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding()
|
||||
.cornerRadius(10)
|
||||
.contentShape(Rectangle()) // 使整个区域可点击
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle()) // 移除按钮默认样式
|
||||
|
||||
// Box
|
||||
Button(action: {
|
||||
print("Box")
|
||||
}) {
|
||||
HStack(spacing: 16) {
|
||||
Image(systemName: "clock.fill")
|
||||
.foregroundColor(.blue)
|
||||
.frame(width: 24, height: 24)
|
||||
Text("My Bind Box")
|
||||
.font(.headline)
|
||||
.foregroundColor(.primary)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding()
|
||||
.cornerRadius(10)
|
||||
.contentShape(Rectangle()) // 使整个区域可点击
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle()) // 移除按钮默认样式
|
||||
|
||||
// setting
|
||||
Button(action: {
|
||||
withAnimation(.spring(response: 0.5, dampingFraction: 0.8)) {
|
||||
showSettings = true
|
||||
}
|
||||
}) {
|
||||
HStack(spacing: 16) {
|
||||
Image(systemName: "person.circle.fill")
|
||||
.foregroundColor(.purple)
|
||||
.frame(width: 24, height: 24)
|
||||
Text("Setting")
|
||||
.font(.headline)
|
||||
.foregroundColor(.primary)
|
||||
Spacer()
|
||||
Image(systemName: "chevron.right")
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(10)
|
||||
.contentShape(Rectangle()) // 使整个区域可点击
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle()) // 移除按钮默认样式
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
// 这里可以添加其他设置项
|
||||
Spacer()
|
||||
}
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
|
||||
// Apply offset to the entire modal when SettingsView is shown
|
||||
.offset(x: showSettings ? UIScreen.main.bounds.width : 0)
|
||||
.animation(.spring(response: 0.5, dampingFraction: 0.8), value: showSettings)
|
||||
|
||||
ZStack {
|
||||
// Semi-transparent overlay for settings
|
||||
if showSettings {
|
||||
Color.black.opacity(0.4)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
.onTapGesture {
|
||||
withAnimation(.spring(response: 0.5, dampingFraction: 0.8)) {
|
||||
showSettings = false
|
||||
}
|
||||
}
|
||||
.transition(.opacity)
|
||||
}
|
||||
|
||||
// Full screen settings view with slide animation
|
||||
if showSettings {
|
||||
SettingsView(isPresented: $showSettings)
|
||||
.transition(.move(edge: .leading))
|
||||
.zIndex(1) // Ensure it's above other content
|
||||
.onAppear {
|
||||
// Reset the navigation path when settings appear
|
||||
navigationPath.removeLast(navigationPath.count)
|
||||
}
|
||||
}
|
||||
}
|
||||
.animation(.spring(response: 0.5, dampingFraction: 0.8), value: showSettings)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#Preview {
|
||||
ContentView()
|
||||
}
|
||||
BIN
wake/Utils/.DS_Store
vendored
Normal file
BIN
wake/Utils/.DS_Store
vendored
Normal file
Binary file not shown.
94
wake/Utils/LoginURLSession.swift
Normal file
94
wake/Utils/LoginURLSession.swift
Normal file
@ -0,0 +1,94 @@
|
||||
import Foundation
|
||||
|
||||
// 可选:定义响应模型(根据你的实际返回结构调整)
|
||||
//struct LoginResponse: Codable {
|
||||
// let code: Int?
|
||||
// let message: String?
|
||||
// let [String: AnyCodable]? // 或具体结构,如 token, userId 等
|
||||
//}
|
||||
|
||||
// 因为 Dictionary<String, Any> 不能直接 Codable,这里用 AnyCodable 简化
|
||||
//enum AnyCodable: Codable {}
|
||||
|
||||
func passwordLogin(username: String, password: String) {
|
||||
guard let url = URL(string: "http://192.168.31.156:31646/api/v1/iam/login/password-login") else {
|
||||
print("❌ 无效的URL")
|
||||
return
|
||||
}
|
||||
|
||||
// 1. 创建 URLRequest
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "POST"
|
||||
|
||||
// 2. 设置请求头
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
// 如果接口需要,也可以加 User-Agent 或其他 header
|
||||
request.setValue("iOS App", forHTTPHeaderField: "User-Agent")
|
||||
|
||||
// 3. 准备参数
|
||||
let parameters = [
|
||||
"account": username,
|
||||
"password": password
|
||||
]
|
||||
|
||||
// 4. 将参数编码为 JSON
|
||||
do {
|
||||
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: [])
|
||||
} catch {
|
||||
print("❌ 参数序列化失败: \(error)")
|
||||
return
|
||||
}
|
||||
|
||||
// 5. 发送请求
|
||||
let task = URLSession.shared.dataTask(with: request) { data, response, error in
|
||||
// 回调在后台线程,更新 UI 需切主线程
|
||||
DispatchQueue.main.async {
|
||||
if let error = error {
|
||||
print("网络错误: \(error)")
|
||||
return
|
||||
}
|
||||
|
||||
// 检查状态码
|
||||
if let httpResponse = response as? HTTPURLResponse {
|
||||
print("状态码: \(httpResponse.statusCode)")
|
||||
|
||||
if httpResponse.statusCode == 404 {
|
||||
print("⚠️ 接口未找到,请确认:")
|
||||
print("1. 后端服务是否正在运行?")
|
||||
print("2. IP 和端口是否正确?")
|
||||
print("3. 网络是否在同一局域网?")
|
||||
return
|
||||
} else if httpResponse.statusCode != 200 {
|
||||
print("服务器错误: \(httpResponse.statusCode)")
|
||||
}
|
||||
}
|
||||
|
||||
// 处理返回数据
|
||||
guard let data = data else {
|
||||
print("❌ 无返回数据")
|
||||
return
|
||||
}
|
||||
|
||||
// 打印原始响应(调试用)
|
||||
if let jsonString = String(data: data, encoding: .utf8) {
|
||||
print("响应内容: \(jsonString)")
|
||||
}
|
||||
|
||||
// 尝试解析为 JSON 字典
|
||||
do {
|
||||
if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
||||
print("解析成功: \(json)")
|
||||
|
||||
// 如果你有明确的模型,可以用 JSONDecoder 解码 LoginResponse
|
||||
// let decoder = JSONDecoder()
|
||||
// let loginResult = try decoder.decode(LoginResponse.self, from: data)
|
||||
}
|
||||
} catch {
|
||||
print("❌ JSON 解析失败: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 启动请求
|
||||
task.resume()
|
||||
}
|
||||
34
wake/Utils/NetWork.swift
Normal file
34
wake/Utils/NetWork.swift
Normal file
@ -0,0 +1,34 @@
|
||||
import SwiftUI
|
||||
|
||||
class Network: ObservableObject {
|
||||
@Published var users: [User] = []
|
||||
|
||||
func getUsers() {
|
||||
guard let url = URL(string: "http://192.168.31.156:31646/api/iam/login/password-login") else { fatalError("Missing URL") }
|
||||
|
||||
let urlRequest = URLRequest(url: url)
|
||||
|
||||
let dataTask = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
|
||||
if let error = error {
|
||||
print("Request error: ", error)
|
||||
return
|
||||
}
|
||||
|
||||
guard let response = response as? HTTPURLResponse else { return }
|
||||
|
||||
if response.statusCode == 200 {
|
||||
guard let data = data else { return }
|
||||
DispatchQueue.main.async {
|
||||
do {
|
||||
let decodedUsers = try JSONDecoder().decode([User].self, from: data)
|
||||
self.users = decodedUsers
|
||||
} catch let error {
|
||||
print("Error decoding: ", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dataTask.resume()
|
||||
}
|
||||
}
|
||||
49
wake/Utils/PasswordLogin.swift
Normal file
49
wake/Utils/PasswordLogin.swift
Normal file
@ -0,0 +1,49 @@
|
||||
import Foundation // 必须导入
|
||||
|
||||
struct LoginResponse: Codable {
|
||||
let token: String?
|
||||
let error: String?
|
||||
}
|
||||
|
||||
func callLoginAPI() {
|
||||
// 1. 创建 URL
|
||||
let urlString = "http://192.168.31.156:31646/api/v1/iam/login/password-login"
|
||||
guard let url = URL(string: urlString) else {
|
||||
print("Invalid URL")
|
||||
return
|
||||
}
|
||||
|
||||
// 2. 准备请求体
|
||||
let body = ["username": "testUser", "password": "test123"]
|
||||
guard let jsonData = try? JSONSerialization.data(withJSONObject: body) else {
|
||||
print("JSON encoding failed")
|
||||
return
|
||||
}
|
||||
print(jsonData)
|
||||
// 3. 配置请求
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "POST"
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
request.httpBody = jsonData
|
||||
print(request)
|
||||
// 4. 发送请求
|
||||
let task = URLSession.shared.dataTask(with: request) { data, response, error in
|
||||
if let error = error {
|
||||
print("Error: \(error.localizedDescription)")
|
||||
return
|
||||
}
|
||||
|
||||
guard let data = data else {
|
||||
print("No data received")
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
let result = try JSONDecoder().decode(LoginResponse.self, from: data)
|
||||
print("Token: \(result.token ?? "nil")")
|
||||
} catch {
|
||||
print("Decoding error: \(error)")
|
||||
}
|
||||
}
|
||||
task.resume()
|
||||
}
|
||||
31
wake/Utils/User.swift
Normal file
31
wake/Utils/User.swift
Normal file
@ -0,0 +1,31 @@
|
||||
import Foundation
|
||||
|
||||
struct User: Identifiable, Decodable {
|
||||
var id: Int
|
||||
var name: String
|
||||
var username: String
|
||||
var email: String
|
||||
var address: Address
|
||||
var phone: String
|
||||
var website: String
|
||||
var company: Company
|
||||
|
||||
struct Address: Decodable {
|
||||
var street: String
|
||||
var suite: String
|
||||
var city: String
|
||||
var zipcode: String
|
||||
var geo: Geo
|
||||
|
||||
struct Geo: Decodable {
|
||||
var lat: String
|
||||
var lng: String
|
||||
}
|
||||
}
|
||||
|
||||
struct Company: Decodable {
|
||||
var name: String
|
||||
var catchPhrase: String
|
||||
var bs: String
|
||||
}
|
||||
}
|
||||
BIN
wake/View/.DS_Store
vendored
Normal file
BIN
wake/View/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
wake/View/Components/.DS_Store
vendored
Normal file
BIN
wake/View/Components/.DS_Store
vendored
Normal file
Binary file not shown.
114
wake/View/Components/Button.swift
Normal file
114
wake/View/Components/Button.swift
Normal file
@ -0,0 +1,114 @@
|
||||
import SwiftUI
|
||||
|
||||
// 按钮类型
|
||||
enum ButtonType {
|
||||
case primary // 主按钮(蓝色)
|
||||
case secondary // 次要按钮(灰色边框)
|
||||
case danger // 危险按钮(红色)
|
||||
}
|
||||
|
||||
// 按钮大小(可选)
|
||||
enum ButtonSize {
|
||||
case small
|
||||
case medium
|
||||
case large
|
||||
}
|
||||
|
||||
struct CustomButton: View {
|
||||
// MARK: - 属性
|
||||
let text: String
|
||||
let type: ButtonType
|
||||
let size: ButtonSize
|
||||
let fullWidth: Bool // 是否占满父容器宽度
|
||||
let isLoading: Bool // 是否加载中
|
||||
let action: () -> Void // 点击回调(无参数)
|
||||
|
||||
// MARK: - 主体视图
|
||||
var body: some View {
|
||||
Button(action: {
|
||||
if !isLoading { // 防止加载时触发
|
||||
action()
|
||||
}
|
||||
}) {
|
||||
if isLoading {
|
||||
// 显示加载动画
|
||||
HStack {
|
||||
ProgressView() // iOS 加载指示器
|
||||
.progressViewStyle(CircularProgressViewStyle(tint: .white))
|
||||
Text("加载中...")
|
||||
}
|
||||
} else {
|
||||
Text(text)
|
||||
.fontWeight(.medium)
|
||||
}
|
||||
}
|
||||
.font(font(for: size))
|
||||
.padding(padding(for: size))
|
||||
.frame(maxWidth: fullWidth ? .infinity : (width(for: size)))
|
||||
.background(backgroundColor)
|
||||
.foregroundColor(.white)
|
||||
.cornerRadius(8)
|
||||
.overlay(
|
||||
// 次要按钮:只显示边框
|
||||
RoundedRectangle(cornerRadius: 8)
|
||||
.stroke(borderColor, lineWidth: 2)
|
||||
.opacity(type == .secondary ? 1 : 0)
|
||||
)
|
||||
.disabled(isLoading) // 加载时禁用点击
|
||||
.opacity(isLoading ? 0.8 : 1.0)
|
||||
}
|
||||
|
||||
// MARK: - 计算属性:背景色
|
||||
private var backgroundColor: Color {
|
||||
switch type {
|
||||
case .primary:
|
||||
return .blue
|
||||
case .secondary:
|
||||
return .clear
|
||||
case .danger:
|
||||
return .red
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 计算属性:边框色
|
||||
private var borderColor: Color {
|
||||
switch type {
|
||||
case .secondary:
|
||||
return .gray
|
||||
default:
|
||||
return .clear
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 辅助函数:字体
|
||||
private func font(for size: ButtonSize) -> Font {
|
||||
switch size {
|
||||
case .small:
|
||||
return .caption
|
||||
case .medium:
|
||||
return .subheadline
|
||||
case .large:
|
||||
return .headline
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 辅助函数:内边距
|
||||
private func padding(for size: ButtonSize) -> EdgeInsets {
|
||||
let horizontal = CGFloat(16)
|
||||
let vertical: CGFloat
|
||||
switch size {
|
||||
case .small:
|
||||
vertical = 6
|
||||
case .medium:
|
||||
vertical = 10
|
||||
case .large:
|
||||
vertical = 14
|
||||
}
|
||||
return EdgeInsets(top: vertical, leading: horizontal, bottom: vertical, trailing: horizontal)
|
||||
}
|
||||
|
||||
// MARK: - 辅助函数:固定宽度(非全宽时)
|
||||
private func width(for size: ButtonSize) -> CGFloat? {
|
||||
return nil // 自适应宽度,除非你想要固定值
|
||||
}
|
||||
}
|
||||
55
wake/View/Components/SheetModal.swift
Normal file
55
wake/View/Components/SheetModal.swift
Normal file
@ -0,0 +1,55 @@
|
||||
import SwiftUI
|
||||
|
||||
struct SlideInModal<Content: View>: View {
|
||||
@Binding var isPresented: Bool
|
||||
let onDismiss: () -> Void
|
||||
let content: () -> Content
|
||||
|
||||
// 动画配置 - 更慢的动画
|
||||
private let animation = Animation.spring(
|
||||
response: 0.8, // 增加响应时间使动画更慢
|
||||
dampingFraction: 0.6, // 减少阻尼系数使弹跳更明显
|
||||
blendDuration: 0.8 // 增加混合时间使过渡更平滑
|
||||
)
|
||||
|
||||
var body: some View {
|
||||
ZStack(alignment: .leading) {
|
||||
// 遮罩背景
|
||||
if isPresented {
|
||||
Color.black
|
||||
.opacity(0.5)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
.transition(.opacity)
|
||||
.zIndex(1)
|
||||
.onTapGesture {
|
||||
withAnimation(animation) {
|
||||
isPresented = false
|
||||
onDismiss()
|
||||
}
|
||||
}
|
||||
|
||||
// 弹窗内容
|
||||
VStack(spacing: 0) {
|
||||
// 顶部安全区域占位
|
||||
Color.clear
|
||||
.frame(height: UIApplication.shared.windows.first?.safeAreaInsets.top ?? 0)
|
||||
|
||||
// 内容区域
|
||||
content()
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.padding(.bottom, UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0)
|
||||
}
|
||||
.frame(width: UIScreen.main.bounds.width * 0.8)
|
||||
.frame(maxHeight: .infinity)
|
||||
.background(Color(.systemBackground))
|
||||
.edgesIgnoringSafeArea(.vertical)
|
||||
.offset(x: isPresented ? 0 : -UIScreen.main.bounds.width)
|
||||
.zIndex(2)
|
||||
.transition(.move(edge: .leading))
|
||||
.animation(animation, value: isPresented)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
}
|
||||
}
|
||||
50
wake/View/Components/TextInput.swift
Normal file
50
wake/View/Components/TextInput.swift
Normal file
@ -0,0 +1,50 @@
|
||||
import SwiftUI
|
||||
|
||||
// 输入框类型枚举(更优雅的方式)
|
||||
enum TextFieldType {
|
||||
case username
|
||||
case password
|
||||
case number
|
||||
case email
|
||||
case text
|
||||
}
|
||||
|
||||
struct CustomTextField: View {
|
||||
// MARK: - 属性
|
||||
let placeholder: String
|
||||
let type: TextFieldType
|
||||
|
||||
// 双向绑定的值(关键!)
|
||||
@Binding var value: String
|
||||
|
||||
// MARK: - 主体视图
|
||||
var body: some View {
|
||||
Group { // 使用 Group 统一修饰符
|
||||
if type == .password {
|
||||
// 密码框
|
||||
SecureField(placeholder, text: $value)
|
||||
} else {
|
||||
// 普通文本框
|
||||
TextField(placeholder, text: $value)
|
||||
.textInputAutocapitalization(.never) // 关闭自动大写
|
||||
.disableAutocorrection(true) // 关闭自动纠错
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemGray6))
|
||||
.cornerRadius(8)
|
||||
.keyboardType(keyboardType(for: type)) // 设置键盘类型
|
||||
}
|
||||
|
||||
// MARK: - 根据类型返回键盘类型
|
||||
private func keyboardType(for type: TextFieldType) -> UIKeyboardType {
|
||||
switch type {
|
||||
case .number:
|
||||
return .numberPad
|
||||
case .email:
|
||||
return .emailAddress
|
||||
default:
|
||||
return .default
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
wake/View/Login/.DS_Store
vendored
Normal file
BIN
wake/View/Login/.DS_Store
vendored
Normal file
Binary file not shown.
270
wake/View/Login/Login.swift
Normal file
270
wake/View/Login/Login.swift
Normal file
@ -0,0 +1,270 @@
|
||||
import SwiftUI
|
||||
import Alamofire
|
||||
|
||||
struct Post: Codable {
|
||||
let id: Int
|
||||
let title: String
|
||||
let body: String
|
||||
let userId: Int
|
||||
}
|
||||
struct Login: Encodable {
|
||||
let account: String
|
||||
let password: String
|
||||
}
|
||||
|
||||
struct LoginView: View {
|
||||
@State private var showModal = false
|
||||
@State private var showSettings = false
|
||||
@State private var contentOffset: CGFloat = 0
|
||||
// 用户名称/邮箱
|
||||
@State private var username=""
|
||||
// 密码
|
||||
@State private var password=""
|
||||
// 登录loading
|
||||
@State private var isLoading=false
|
||||
|
||||
// 登录点击事件
|
||||
func handleLogin() {
|
||||
withAnimation {
|
||||
isLoading = true
|
||||
}
|
||||
// 示例调用
|
||||
passwordLogin(username: "jyq@memo.cn", password: "111111")
|
||||
// 注意:这里应该在网络请求完成后设置 isLoading = false
|
||||
// 例如在 passwordLogin 的回调中
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
withAnimation {
|
||||
self.isLoading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get 请求
|
||||
func get(){
|
||||
AF.request("http://192.168.31.156:31646/api/v1/iam/access-token-refresh").response { response in
|
||||
debugPrint(response)
|
||||
}
|
||||
}
|
||||
// post 请求
|
||||
func post(){
|
||||
let login = Login(account: username, password: password)
|
||||
print(login)
|
||||
AF.request("http://192.168.31.156:31646/api/v1/iam/login/password-login", method: .post,parameters: login).response{
|
||||
response in debugPrint(response)
|
||||
}
|
||||
}
|
||||
|
||||
// func createPost() {
|
||||
// Task {
|
||||
// do {
|
||||
// print("12132412354365342")
|
||||
// let newPost = try await NetworkManager.shared.request(
|
||||
// endpoint: "/iam/login/password-login",
|
||||
// method: .post,
|
||||
// parameters: [
|
||||
// "account": username,
|
||||
// "password": password
|
||||
// ]
|
||||
// ) as Post
|
||||
// // 在字符串中添加变量 \(变量)
|
||||
// print("登录成功:$newPost.id)\(username)")
|
||||
// } catch {
|
||||
// print("登录失败:$error)")
|
||||
//
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
var body: some View {
|
||||
ZStack {
|
||||
// Main content with slide effect
|
||||
VStack {
|
||||
VStack(spacing: 20) {
|
||||
// This spacer ensures content stays below the status bar
|
||||
Spacer().frame(height: UIApplication.shared.windows.first?.safeAreaInsets.top ?? 0)
|
||||
// 顶部栏
|
||||
HStack {
|
||||
Spacer()
|
||||
Button(action: {
|
||||
withAnimation(.spring(response: 0.5, dampingFraction: 0.8)) {
|
||||
showModal = true
|
||||
}
|
||||
}) {
|
||||
Image(systemName: "gearshape")
|
||||
.font(.title2)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
Text("邮箱登录").font(.title)
|
||||
|
||||
// 登录表单
|
||||
VStack {
|
||||
// 账号
|
||||
CustomTextField(
|
||||
placeholder: "请输入用户名",
|
||||
type: .username,
|
||||
value: $username
|
||||
)
|
||||
// 密码
|
||||
CustomTextField(
|
||||
placeholder: "请输入密码",
|
||||
type: .password,
|
||||
value: $password
|
||||
)
|
||||
// 登录
|
||||
CustomButton(
|
||||
text: "登录",
|
||||
type: .primary,
|
||||
size: .large,
|
||||
fullWidth: true,
|
||||
isLoading: isLoading
|
||||
) {
|
||||
handleLogin()
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
Spacer()
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.background(Color(.systemBackground))
|
||||
.offset(x: showModal ? UIScreen.main.bounds.width * 0.35 : 0)
|
||||
.animation(.spring(response: 0.5, dampingFraction: 0.8), value: showModal)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
}
|
||||
|
||||
// 添加半透明遮罩层
|
||||
if showModal {
|
||||
Color.black.opacity(0.4)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
.onTapGesture {
|
||||
withAnimation(.spring(response: 0.5, dampingFraction: 0.8)) {
|
||||
showModal = false
|
||||
}
|
||||
}
|
||||
.transition(.opacity)
|
||||
}
|
||||
|
||||
// Modal with animation
|
||||
SlideInModal(isPresented: $showModal, onDismiss: {
|
||||
withAnimation(.spring(response: 0.5, dampingFraction: 0.8)) {
|
||||
showModal = false
|
||||
}
|
||||
}) {
|
||||
VStack(spacing: 20) {
|
||||
// 用户信息区域
|
||||
HStack(alignment: .center, spacing: 16) {
|
||||
// 头像
|
||||
Image(systemName: "person.circle.fill")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
.frame(width: 60, height: 60)
|
||||
.foregroundColor(.blue)
|
||||
.clipShape(Circle())
|
||||
|
||||
// 姓名和ID
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text("用户名")
|
||||
.font(.headline)
|
||||
.foregroundColor(.primary)
|
||||
|
||||
Text("ID: 12345678")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.top, 16)
|
||||
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text("会员等级")
|
||||
.font(.headline)
|
||||
.foregroundColor(.primary)
|
||||
Text("会员时间")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
Text("会员中心")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(16)
|
||||
.background(Color(red: 0.92, green: 0.92, blue: 0.92))
|
||||
.cornerRadius(10)
|
||||
.padding(.horizontal, 16)
|
||||
|
||||
VStack(spacing: 12) {
|
||||
// memories
|
||||
Button(action: {
|
||||
print("memories")
|
||||
}) {
|
||||
HStack(spacing: 16) {
|
||||
Image(systemName: "crown.fill")
|
||||
.foregroundColor(.orange)
|
||||
.frame(width: 24, height: 24)
|
||||
|
||||
Text("My Memories")
|
||||
.font(.headline)
|
||||
.foregroundColor(.primary)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding()
|
||||
.cornerRadius(10)
|
||||
.contentShape(Rectangle()) // 使整个区域可点击
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle()) // 移除按钮默认样式
|
||||
|
||||
// Box
|
||||
Button(action: {
|
||||
print("Box")
|
||||
}) {
|
||||
HStack(spacing: 16) {
|
||||
Image(systemName: "clock.fill")
|
||||
.foregroundColor(.blue)
|
||||
.frame(width: 24, height: 24)
|
||||
Text("My Bind Box")
|
||||
.font(.headline)
|
||||
.foregroundColor(.primary)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding()
|
||||
.cornerRadius(10)
|
||||
.contentShape(Rectangle()) // 使整个区域可点击
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle()) // 移除按钮默认样式
|
||||
|
||||
// setting
|
||||
Button(action: {
|
||||
print("Setting")
|
||||
}) {
|
||||
HStack(spacing: 16) {
|
||||
Image(systemName: "person.circle.fill")
|
||||
.foregroundColor(.purple)
|
||||
.frame(width: 24, height: 24)
|
||||
Text("Setting")
|
||||
.font(.headline)
|
||||
.foregroundColor(.primary)
|
||||
Spacer()
|
||||
}
|
||||
.padding()
|
||||
.cornerRadius(10)
|
||||
.contentShape(Rectangle()) // 使整个区域可点击
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle()) // 移除按钮默认样式
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
// 这里可以添加其他设置项
|
||||
Spacer()
|
||||
}
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
LoginView()
|
||||
}
|
||||
BIN
wake/View/Owner/.DS_Store
vendored
Normal file
BIN
wake/View/Owner/.DS_Store
vendored
Normal file
Binary file not shown.
111
wake/View/Owner/ModalContentView.swift
Normal file
111
wake/View/Owner/ModalContentView.swift
Normal file
@ -0,0 +1,111 @@
|
||||
import SwiftUI
|
||||
|
||||
struct ModalContentView: View {
|
||||
let goBack: () -> Void
|
||||
@Environment(\.dismiss) private var dismissModal
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 20) {
|
||||
// 用户信息
|
||||
HStack(alignment: .center, spacing: 16) {
|
||||
Image(systemName: "person.circle.fill")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
.frame(width: 60, height: 60)
|
||||
.foregroundColor(.blue)
|
||||
.clipShape(Circle())
|
||||
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text("用户名")
|
||||
.font(.headline)
|
||||
Text("ID: 12345678")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.top, 16)
|
||||
|
||||
// 会员区域
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text("会员等级").font(.headline)
|
||||
Text("会员时间").font(.subheadline).foregroundColor(.secondary)
|
||||
Text("会员中心").font(.subheadline).foregroundColor(.secondary)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(16)
|
||||
.background(Color(red: 0.92, green: 0.92, blue: 0.92))
|
||||
.cornerRadius(10)
|
||||
.padding(.horizontal, 16)
|
||||
|
||||
// 功能按钮
|
||||
VStack(spacing: 12) {
|
||||
ModalButton(icon: "crown.fill", color: .orange, text: "My Memories")
|
||||
ModalButton(icon: "clock.fill", color: .blue, text: "My Bind Box")
|
||||
|
||||
// 跳转设置页
|
||||
Button(action: {
|
||||
goBack()
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
||||
// 模拟从左边滑入的效果
|
||||
}
|
||||
}) {
|
||||
HStack(spacing: 16) {
|
||||
Image(systemName: "person.circle.fill")
|
||||
.foregroundColor(.purple)
|
||||
.frame(width: 24, height: 24)
|
||||
Text("Setting")
|
||||
.font(.headline)
|
||||
.foregroundColor(.primary)
|
||||
Spacer()
|
||||
Image(systemName: "chevron.right")
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(10)
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 按钮组件
|
||||
struct ModalButton: View {
|
||||
let icon: String
|
||||
let color: Color
|
||||
let text: String
|
||||
let action: () -> Void
|
||||
|
||||
init(icon: String, color: Color, text: String, action: @escaping () -> Void = {}) {
|
||||
self.icon = icon
|
||||
self.color = color
|
||||
self.text = text
|
||||
self.action = action
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Button(action: action) {
|
||||
HStack(spacing: 16) {
|
||||
Image(systemName: icon)
|
||||
.foregroundColor(color)
|
||||
.frame(width: 24, height: 24)
|
||||
Text(text)
|
||||
.font(.headline)
|
||||
.foregroundColor(.primary)
|
||||
Spacer()
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(10)
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
}
|
||||
}
|
||||
201
wake/View/Owner/SettingsView.swift
Normal file
201
wake/View/Owner/SettingsView.swift
Normal file
@ -0,0 +1,201 @@
|
||||
import SwiftUI
|
||||
|
||||
struct SettingsView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@State private var isAppeared = false
|
||||
@Binding var isPresented: Bool
|
||||
|
||||
// Animation configuration
|
||||
private let animation = Animation.spring(
|
||||
response: 0.8,
|
||||
dampingFraction: 0.6,
|
||||
blendDuration: 0.8
|
||||
)
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
// Custom navigation bar
|
||||
HStack {
|
||||
Button(action: {
|
||||
withAnimation(.spring(response: 0.5, dampingFraction: 0.8)) {
|
||||
isPresented = false
|
||||
}
|
||||
}) {
|
||||
HStack(spacing: 4) {
|
||||
Image(systemName: "chevron.left")
|
||||
.font(.system(size: 17, weight: .semibold))
|
||||
Text("Back")
|
||||
}
|
||||
.foregroundColor(.blue)
|
||||
.padding()
|
||||
}
|
||||
Spacer()
|
||||
Text("Settings")
|
||||
.font(.headline)
|
||||
.padding()
|
||||
Spacer()
|
||||
// Invisible view to balance the layout
|
||||
Color.clear
|
||||
.frame(width: 44, height: 44)
|
||||
}
|
||||
.background(Color(.systemBackground))
|
||||
|
||||
// Settings content
|
||||
List(0..<1) { _ in
|
||||
// This empty section ensures proper spacing
|
||||
Section {
|
||||
EmptyView()
|
||||
} header: {
|
||||
EmptyView()
|
||||
}
|
||||
// Add an invisible section header to remove extra top padding
|
||||
Section(header: EmptyView()) {
|
||||
EmptyView()
|
||||
}
|
||||
// Account & Security
|
||||
HStack {
|
||||
Color.clear
|
||||
.frame(width: 12, height: 24)
|
||||
.background(Color(.systemBackground))
|
||||
Image(systemName: "person.crop.circle")
|
||||
.font(.system(size: 24))
|
||||
.foregroundColor(.gray)
|
||||
Text("Account & Security")
|
||||
Spacer()
|
||||
Image(systemName: "chevron.right")
|
||||
.foregroundColor(.gray)
|
||||
Color.clear
|
||||
.frame(width: 12, height: 24)
|
||||
.background(Color(.systemBackground))
|
||||
}
|
||||
.listRowBackground(Color(.systemBackground))
|
||||
|
||||
// Permission Management
|
||||
HStack {
|
||||
Color.clear
|
||||
.frame(width: 12, height: 24)
|
||||
.background(Color(.systemBackground))
|
||||
Image(systemName: "lock.shield")
|
||||
.font(.system(size: 24))
|
||||
.foregroundColor(.gray)
|
||||
Text("Permission Management")
|
||||
Spacer()
|
||||
Image(systemName: "chevron.right")
|
||||
.foregroundColor(.gray)
|
||||
Color.clear
|
||||
.frame(width: 12, height: 24)
|
||||
.background(Color(.systemBackground))
|
||||
}
|
||||
.listRowBackground(Color(.systemBackground))
|
||||
|
||||
// Support & Service
|
||||
HStack {
|
||||
Color.clear
|
||||
.frame(width: 12, height: 24)
|
||||
.background(Color(.systemBackground))
|
||||
Image(systemName: "questionmark.circle")
|
||||
.font(.system(size: 24))
|
||||
.foregroundColor(.gray)
|
||||
Text("Support & Service")
|
||||
Spacer()
|
||||
Image(systemName: "chevron.right")
|
||||
.foregroundColor(.gray)
|
||||
Color.clear
|
||||
.frame(width: 12, height: 24)
|
||||
.background(Color(.systemBackground))
|
||||
}
|
||||
.listRowBackground(Color(.systemBackground))
|
||||
|
||||
// About Us
|
||||
HStack {
|
||||
Color.clear
|
||||
.frame(width: 12, height: 24)
|
||||
.background(Color(.systemBackground))
|
||||
Image(systemName: "info.circle")
|
||||
.font(.system(size: 24))
|
||||
.foregroundColor(.gray)
|
||||
Text("About Us")
|
||||
Spacer()
|
||||
Image(systemName: "chevron.right")
|
||||
.foregroundColor(.gray)
|
||||
Color.clear
|
||||
.frame(width: 12, height: 24)
|
||||
.background(Color(.systemBackground))
|
||||
}
|
||||
.listRowBackground(Color(.systemBackground))
|
||||
}
|
||||
.listStyle(GroupedListStyle())
|
||||
.navigationTitle("Setting")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.background(Color(.systemGray6))
|
||||
.environment(\.horizontalSizeClass, .regular)
|
||||
.environment(\.defaultMinListRowHeight, 50)
|
||||
.listRowInsets(EdgeInsets())
|
||||
.onAppear {
|
||||
// Remove extra separators below the list
|
||||
UITableView.appearance().tableFooterView = UIView()
|
||||
// Remove separator inset
|
||||
UITableView.appearance().separatorInset = .zero
|
||||
// Remove extra space at the top of the table view
|
||||
UITableView.appearance().contentInset = .zero
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
Button(action: {
|
||||
withAnimation(.spring(response: 0.5, dampingFraction: 0.8)) {
|
||||
isAppeared = false
|
||||
}
|
||||
// Delay the dismiss to allow the animation to complete
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
||||
isPresented = false
|
||||
}
|
||||
}) {
|
||||
HStack(spacing: 4) {
|
||||
Image(systemName: "chevron.left")
|
||||
.font(.system(size: 16, weight: .medium))
|
||||
.foregroundColor(.blue)
|
||||
Text("Back")
|
||||
.font(.system(size: 17, weight: .regular))
|
||||
.foregroundColor(.blue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.animation(animation, value: isAppeared)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Preview
|
||||
#Preview {
|
||||
NavigationView {
|
||||
SettingsView(isPresented: .constant(true))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Subviews
|
||||
struct AccountSecurityView: View {
|
||||
var body: some View {
|
||||
Text("Account & Security")
|
||||
}
|
||||
}
|
||||
|
||||
struct PermissionManagementView: View {
|
||||
var body: some View {
|
||||
Text("Permission Management")
|
||||
}
|
||||
}
|
||||
|
||||
struct SupportServiceView: View {
|
||||
var body: some View {
|
||||
Text("Support & Service")
|
||||
}
|
||||
}
|
||||
|
||||
struct AboutUsView: View {
|
||||
var body: some View {
|
||||
Text("About Us")
|
||||
}
|
||||
}
|
||||
29
wake/WakeApp.swift
Normal file
29
wake/WakeApp.swift
Normal file
@ -0,0 +1,29 @@
|
||||
//
|
||||
// WakeApp.swift
|
||||
// Wake
|
||||
//
|
||||
// Created by elliwood on 2025/8/11.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct WakeApp: App {
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
// SettingsView()
|
||||
// 导航栏按钮
|
||||
// TabView{
|
||||
// ContentView()
|
||||
// .tabItem{
|
||||
// Label("wake", systemImage: "book")
|
||||
// }
|
||||
// SettingView()
|
||||
// .tabItem{
|
||||
// Label("setting", systemImage: "gear")
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user