动效
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