diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ddb0340..cfe0d21 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,9 +17,9 @@ jobs: strategy: matrix: destination: - - platform=iOS Simulator,name=iPhone 16,OS=18.0 - - platform=watchOS Simulator,name=Apple Watch SE (40mm) (2nd generation),OS=11.0 - - platform=tvOS Simulator,name=Apple TV 4K (3rd generation) (at 1080p),OS=18.0 + - platform=iOS Simulator,name=iPhone 16,OS=18.1 + - platform=watchOS Simulator,name=Apple Watch SE (40mm) (2nd generation),OS=11.1 + - platform=tvOS Simulator,name=Apple TV 4K (3rd generation) (at 1080p),OS=18.1 - platform=macOS,arch=arm64 with: package: OversizeUI @@ -33,8 +33,8 @@ jobs: strategy: matrix: destination: - - platform=iOS Simulator,name=iPhone 16 Pro,OS=18.0 - - platform=iOS Simulator,name=iPad (10th generation),OS=18.0 + - platform=iOS Simulator,name=iPhone 16 Pro,OS=18.1 + - platform=iOS Simulator,name=iPad (10th generation),OS=18.1 with: path: Example/Example scheme: Example (iOS) @@ -58,8 +58,8 @@ jobs: strategy: matrix: destination: - - platform=tvOS Simulator,name=Apple TV 4K (3rd generation) (at 1080p),OS=18.0 - - platform=tvOS Simulator,name=Apple TV 4K (3rd generation),OS=18.0 + - platform=tvOS Simulator,name=Apple TV 4K (3rd generation) (at 1080p),OS=18.1 + - platform=tvOS Simulator,name=Apple TV 4K (3rd generation),OS=18.1 with: path: Example/Example scheme: Example (tvOS) @@ -73,7 +73,7 @@ jobs: strategy: matrix: destination: - - platform=watchOS Simulator,name=Apple Watch SE (40mm) (2nd generation),OS=11.0 + - platform=watchOS Simulator,name=Apple Watch SE (40mm) (2nd generation),OS=11.1 with: path: Example/Example scheme: Example (watchOS) diff --git a/Example/Example WatchKit App/Info.plist b/Example/Example WatchKit App/Info.plist index 32e1d08..03a76ab 100644 --- a/Example/Example WatchKit App/Info.plist +++ b/Example/Example WatchKit App/Info.plist @@ -20,12 +20,16 @@ 1.0 CFBundleVersion 1 + CLKComplicationPrincipalClass + $(PRODUCT_MODULE_NAME).ComplicationController UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown - WKWatchKitApp + WKApplication + + WKWatchOnly diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj index d445862..bb6f3ce 100644 --- a/Example/Example.xcodeproj/project.pbxproj +++ b/Example/Example.xcodeproj/project.pbxproj @@ -9,7 +9,6 @@ /* Begin PBXBuildFile section */ 1101694E26E41DE10035CB67 /* Example (watchOS).app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 1101694D26E41DE10035CB67 /* Example (watchOS).app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 1101695326E41DE30035CB67 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1101695226E41DE30035CB67 /* Assets.xcassets */; }; - 1101695A26E41DE40035CB67 /* Example WatchKit Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 1101695926E41DE40035CB67 /* Example WatchKit Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 1101696326E41DE40035CB67 /* NotificationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1101696226E41DE40035CB67 /* NotificationController.swift */; }; 1101696526E41DE40035CB67 /* NotificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1101696426E41DE40035CB67 /* NotificationView.swift */; }; 1101696726E41DE40035CB67 /* ComplicationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1101696626E41DE40035CB67 /* ComplicationController.swift */; }; @@ -91,26 +90,19 @@ remoteGlobalIDString = 1101694C26E41DE10035CB67; remoteInfo = "Example WatchKit App"; }; - 1101695B26E41DE40035CB67 /* PBXContainerItemProxy */ = { + 1101697F26E41DE50035CB67 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 84D9F9DC26BDE4EA00B2FCA2 /* Project object */; proxyType = 1; - remoteGlobalIDString = 1101695826E41DE40035CB67; - remoteInfo = "Example WatchKit Extension"; + remoteGlobalIDString = 1101694C26E41DE10035CB67; + remoteInfo = "Example WatchKit App"; }; - 1101697426E41DE50035CB67 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 84D9F9DC26BDE4EA00B2FCA2 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 1101695826E41DE40035CB67; - remoteInfo = "Example WatchKit Extension"; - }; - 1101697F26E41DE50035CB67 /* PBXContainerItemProxy */ = { + 848111842CDFFFDE004D65FE /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 84D9F9DC26BDE4EA00B2FCA2 /* Project object */; proxyType = 1; remoteGlobalIDString = 1101694C26E41DE10035CB67; - remoteInfo = "Example WatchKit App"; + remoteInfo = "Example (watchOS)"; }; /* End PBXContainerItemProxy section */ @@ -126,17 +118,6 @@ name = "Embed Watch Content"; runOnlyForDeploymentPostprocessing = 0; }; - 1101698A26E41DE50035CB67 /* Embed App Extensions */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 13; - files = ( - 1101695A26E41DE40035CB67 /* Example WatchKit Extension.appex in Embed App Extensions */, - ); - name = "Embed App Extensions"; - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -144,7 +125,6 @@ 1101694D26E41DE10035CB67 /* Example (watchOS).app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Example (watchOS).app"; sourceTree = BUILT_PRODUCTS_DIR; }; 1101695226E41DE30035CB67 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 1101695426E41DE30035CB67 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 1101695926E41DE40035CB67 /* Example WatchKit Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Example WatchKit Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; 1101696226E41DE40035CB67 /* NotificationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationController.swift; sourceTree = ""; }; 1101696426E41DE40035CB67 /* NotificationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationView.swift; sourceTree = ""; }; 1101696626E41DE40035CB67 /* ComplicationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComplicationController.swift; sourceTree = ""; }; @@ -369,7 +349,6 @@ 84D9F9F026BDE4EB00B2FCA2 /* Example.app */, 1101694A26E41DE10035CB67 /* Example.app */, 1101694D26E41DE10035CB67 /* Example (watchOS).app */, - 1101695926E41DE40035CB67 /* Example WatchKit Extension.appex */, 1101697326E41DE40035CB67 /* ExampleTests.xctest */, 1101697E26E41DE50035CB67 /* ExampleUITests.xctest */, 1101699E26E423260035CB67 /* Example (tvOS).app */, @@ -419,37 +398,17 @@ buildConfigurationList = 1101699226E41DE50035CB67 /* Build configuration list for PBXNativeTarget "Example (watchOS)" */; buildPhases = ( 1101694B26E41DE10035CB67 /* Resources */, - 1101698A26E41DE50035CB67 /* Embed App Extensions */, + 1101695526E41DE40035CB67 /* Sources */, + 1101695626E41DE40035CB67 /* Frameworks */, ); buildRules = ( ); dependencies = ( - 1101695C26E41DE40035CB67 /* PBXTargetDependency */, ); name = "Example (watchOS)"; productName = "Example WatchKit App"; productReference = 1101694D26E41DE10035CB67 /* Example (watchOS).app */; - productType = "com.apple.product-type.application.watchapp2"; - }; - 1101695826E41DE40035CB67 /* Example WatchKit Extension */ = { - isa = PBXNativeTarget; - buildConfigurationList = 1101699126E41DE50035CB67 /* Build configuration list for PBXNativeTarget "Example WatchKit Extension" */; - buildPhases = ( - 1101695526E41DE40035CB67 /* Sources */, - 1101695626E41DE40035CB67 /* Frameworks */, - 1101695726E41DE40035CB67 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "Example WatchKit Extension"; - packageProductDependencies = ( - 114C9CCA26ECC74A00520E04 /* OversizeUI */, - ); - productName = "Example WatchKit Extension"; - productReference = 1101695926E41DE40035CB67 /* Example WatchKit Extension.appex */; - productType = "com.apple.product-type.watchkit2-extension"; + productType = "com.apple.product-type.application"; }; 1101697226E41DE40035CB67 /* ExampleTests */ = { isa = PBXNativeTarget; @@ -462,7 +421,7 @@ buildRules = ( ); dependencies = ( - 1101697526E41DE50035CB67 /* PBXTargetDependency */, + 848111852CDFFFDE004D65FE /* PBXTargetDependency */, ); name = ExampleTests; productName = ExampleTests; @@ -553,8 +512,9 @@ 84D9F9DC26BDE4EA00B2FCA2 /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 1250; - LastUpgradeCheck = 1330; + LastUpgradeCheck = 1610; TargetAttributes = { 1101694926E41DE10035CB67 = { CreatedOnToolsVersion = 12.5.1; @@ -563,12 +523,9 @@ CreatedOnToolsVersion = 12.5.1; LastSwiftMigration = 1250; }; - 1101695826E41DE40035CB67 = { - CreatedOnToolsVersion = 12.5.1; - }; 1101697226E41DE40035CB67 = { CreatedOnToolsVersion = 12.5.1; - TestTargetID = 1101695826E41DE40035CB67; + TestTargetID = 1101694C26E41DE10035CB67; }; 1101697D26E41DE50035CB67 = { CreatedOnToolsVersion = 12.5.1; @@ -602,7 +559,6 @@ 84D9F9EF26BDE4EB00B2FCA2 /* Example (macOS) */, 1101694926E41DE10035CB67 /* Example */, 1101694C26E41DE10035CB67 /* Example (watchOS) */, - 1101695826E41DE40035CB67 /* Example WatchKit Extension */, 1101697226E41DE40035CB67 /* ExampleTests */, 1101697D26E41DE50035CB67 /* ExampleUITests */, 1101699D26E423260035CB67 /* Example (tvOS) */, @@ -619,19 +575,12 @@ runOnlyForDeploymentPostprocessing = 0; }; 1101694B26E41DE10035CB67 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 1101695326E41DE30035CB67 /* Assets.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 1101695726E41DE40035CB67 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 1101696C26E41DE40035CB67 /* Preview Assets.xcassets in Resources */, 1101696926E41DE40035CB67 /* Assets.xcassets in Resources */, + 1101695326E41DE30035CB67 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -788,21 +737,16 @@ target = 1101694C26E41DE10035CB67 /* Example (watchOS) */; targetProxy = 1101694F26E41DE10035CB67 /* PBXContainerItemProxy */; }; - 1101695C26E41DE40035CB67 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 1101695826E41DE40035CB67 /* Example WatchKit Extension */; - targetProxy = 1101695B26E41DE40035CB67 /* PBXContainerItemProxy */; - }; - 1101697526E41DE50035CB67 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 1101695826E41DE40035CB67 /* Example WatchKit Extension */; - targetProxy = 1101697426E41DE50035CB67 /* PBXContainerItemProxy */; - }; 1101698026E41DE50035CB67 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 1101694C26E41DE10035CB67 /* Example (watchOS) */; targetProxy = 1101697F26E41DE50035CB67 /* PBXContainerItemProxy */; }; + 848111852CDFFFDE004D65FE /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 1101694C26E41DE10035CB67 /* Example (watchOS) */; + targetProxy = 848111842CDFFFDE004D65FE /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -817,7 +761,7 @@ PRODUCT_BUNDLE_IDENTIFIER = romanov.cc.Example; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; }; name = Debug; }; @@ -832,7 +776,7 @@ PRODUCT_BUNDLE_IDENTIFIER = romanov.cc.Example; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; VALIDATE_PRODUCT = YES; }; name = Release; @@ -840,25 +784,23 @@ 1101698826E41DE50035CB67 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = ER582ZK85C; - IBSC_MODULE = Example_WatchKit_Extension; INFOPLIST_FILE = "Example WatchKit App/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/../Frameworks", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = romanov.cc.Example.watchkitapp; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = 4; WATCHOS_DEPLOYMENT_TARGET = 9.0; }; @@ -867,73 +809,22 @@ 1101698926E41DE50035CB67 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = ER582ZK85C; - IBSC_MODULE = Example_WatchKit_Extension; INFOPLIST_FILE = "Example WatchKit App/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = romanov.cc.Example.watchkitapp; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = watchos; - SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 4; - VALIDATE_PRODUCT = YES; - WATCHOS_DEPLOYMENT_TARGET = 9.0; - }; - name = Release; - }; - 1101698B26E41DE50035CB67 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_ASSET_PATHS = "\"Example WatchKit Extension/Preview Content\""; - DEVELOPMENT_TEAM = ER582ZK85C; - ENABLE_PREVIEWS = YES; - INFOPLIST_FILE = "Example WatchKit Extension/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = romanov.cc.Example.watchkitapp.watchkitextension; - PRODUCT_NAME = "${TARGET_NAME}"; - SDKROOT = watchos; - SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 4; - WATCHOS_DEPLOYMENT_TARGET = 9.0; - }; - name = Debug; - }; - 1101698C26E41DE50035CB67 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_ASSET_PATHS = "\"Example WatchKit Extension/Preview Content\""; - DEVELOPMENT_TEAM = ER582ZK85C; - ENABLE_PREVIEWS = YES; - INFOPLIST_FILE = "Example WatchKit Extension/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = romanov.cc.Example.watchkitapp.watchkitextension; - PRODUCT_NAME = "${TARGET_NAME}"; + PRODUCT_BUNDLE_IDENTIFIER = romanov.cc.Example.watchkitapp; + PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = 4; VALIDATE_PRODUCT = YES; WATCHOS_DEPLOYMENT_TARGET = 9.0; @@ -943,7 +834,6 @@ 1101698D26E41DE50035CB67 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = ER582ZK85C; @@ -956,9 +846,9 @@ PRODUCT_BUNDLE_IDENTIFIER = romanov.cc.ExampleTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = 4; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example WatchKit Extension.appex/Example WatchKit Extension"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example (watchOS).app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Example (watchOS)"; WATCHOS_DEPLOYMENT_TARGET = 9.0; }; name = Debug; @@ -966,7 +856,6 @@ 1101698E26E41DE50035CB67 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = ER582ZK85C; @@ -979,9 +868,9 @@ PRODUCT_BUNDLE_IDENTIFIER = romanov.cc.ExampleTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = 4; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example WatchKit Extension.appex/Example WatchKit Extension"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example (watchOS).app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Example (watchOS)"; VALIDATE_PRODUCT = YES; WATCHOS_DEPLOYMENT_TARGET = 9.0; }; @@ -1001,7 +890,7 @@ PRODUCT_BUNDLE_IDENTIFIER = romanov.cc.ExampleUITests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = 4; TEST_TARGET_NAME = "Example WatchKit App"; WATCHOS_DEPLOYMENT_TARGET = 9.0; @@ -1022,7 +911,7 @@ PRODUCT_BUNDLE_IDENTIFIER = romanov.cc.ExampleUITests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = 4; TEST_TARGET_NAME = "Example WatchKit App"; VALIDATE_PRODUCT = YES; @@ -1045,7 +934,7 @@ PRODUCT_BUNDLE_IDENTIFIER = romanov.cc.ExampletvOS; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 15.0; }; @@ -1066,7 +955,7 @@ PRODUCT_BUNDLE_IDENTIFIER = romanov.cc.ExampletvOS; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 15.0; VALIDATE_PRODUCT = YES; @@ -1077,6 +966,7 @@ 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++14"; @@ -1107,9 +997,11 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -1136,6 +1028,7 @@ 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++14"; @@ -1166,9 +1059,11 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -1201,7 +1096,7 @@ PRODUCT_BUNDLE_IDENTIFIER = romanov.cc.Example; PRODUCT_NAME = Example; SDKROOT = iphoneos; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -1223,7 +1118,7 @@ PRODUCT_BUNDLE_IDENTIFIER = romanov.cc.Example; PRODUCT_NAME = Example; SDKROOT = iphoneos; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -1238,6 +1133,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ER582ZK85C; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; @@ -1246,11 +1142,11 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 13.0; + MACOSX_DEPLOYMENT_TARGET = 14.0; PRODUCT_BUNDLE_IDENTIFIER = romanov.cc.Example; PRODUCT_NAME = Example; SDKROOT = macosx; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; }; name = Debug; }; @@ -1263,6 +1159,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ER582ZK85C; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; @@ -1271,26 +1168,17 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 13.0; + MACOSX_DEPLOYMENT_TARGET = 14.0; PRODUCT_BUNDLE_IDENTIFIER = romanov.cc.Example; PRODUCT_NAME = Example; SDKROOT = macosx; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 1101699126E41DE50035CB67 /* Build configuration list for PBXNativeTarget "Example WatchKit Extension" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1101698B26E41DE50035CB67 /* Debug */, - 1101698C26E41DE50035CB67 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 1101699226E41DE50035CB67 /* Build configuration list for PBXNativeTarget "Example (watchOS)" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Example/Example.xcodeproj/xcshareddata/xcschemes/Example (iOS).xcscheme b/Example/Example.xcodeproj/xcshareddata/xcschemes/Example (iOS).xcscheme index 4a05217..28cd8ba 100644 --- a/Example/Example.xcodeproj/xcshareddata/xcschemes/Example (iOS).xcscheme +++ b/Example/Example.xcodeproj/xcshareddata/xcschemes/Example (iOS).xcscheme @@ -1,6 +1,6 @@ Avatar { + func avatarStroke(_ strokeColor: Color = .surfacePrimary, lineWidth: CGFloat = 2) -> Avatar { var control = self control.strokeColor = strokeColor + control.strokeLineWidth = lineWidth return control } } diff --git a/Sources/OversizeUI/Controls/Avatar/AvatarPreview.swift b/Sources/OversizeUI/Controls/Avatar/AvatarPreview.swift index 64dfe59..1f30835 100644 --- a/Sources/OversizeUI/Controls/Avatar/AvatarPreview.swift +++ b/Sources/OversizeUI/Controls/Avatar/AvatarPreview.swift @@ -1,6 +1,6 @@ // // Copyright © 2023 Aleksandr Romanov -// File.swift, created on 10.03.2023 +// AvatarPreview.swift, created on 10.03.2023 // import SwiftUI diff --git a/Sources/OversizeUI/Controls/Button/BarButton.swift b/Sources/OversizeUI/Controls/Button/BarButton.swift index 488a089..542974d 100644 --- a/Sources/OversizeUI/Controls/Button/BarButton.swift +++ b/Sources/OversizeUI/Controls/Button/BarButton.swift @@ -133,17 +133,17 @@ public struct BarButton: View { case let .primary(text, _): Text(text) case .closeAction: - IconDeprecated(.xMini, color: .onSurfaceMediumEmphasis) + IconDeprecated(.xMini, color: .onSurfaceSecondary) case .backAction: - IconDeprecated(.arrowLeft, color: .onSurfaceMediumEmphasis) + IconDeprecated(.arrowLeft, color: .onSurfaceSecondary) case let .disabled(text): Text(text) case let .image(image, _): image .renderingMode(.template) - .onSurfaceHighEmphasisForegroundColor() + .onSurfacePrimaryForeground() case let .icon(icon, _): - IconDeprecated(icon, color: .onSurfaceMediumEmphasis) + IconDeprecated(icon, color: .onSurfaceSecondary) } } } diff --git a/Sources/OversizeUI/Controls/Button/Button.swift b/Sources/OversizeUI/Controls/Button/Button.swift index a8c64f6..0e4a645 100644 --- a/Sources/OversizeUI/Controls/Button/Button.swift +++ b/Sources/OversizeUI/Controls/Button/Button.swift @@ -56,7 +56,7 @@ public struct OversizeButtonStyle: ButtonStyle { .opacity(backgroundOpacity)) .overlay { Capsule() - .strokeBorder(Color.onSurfaceHighEmphasis.opacity(0.15), lineWidth: 2) + .strokeBorder(Color.onSurfacePrimary.opacity(0.15), lineWidth: 2) .opacity(isBordered || theme.borderButtons ? 1 : 0) } @@ -66,7 +66,7 @@ public struct OversizeButtonStyle: ButtonStyle { .opacity(backgroundOpacity)) .overlay { RoundedRectangle(cornerRadius: radius != .medium ? radius.rawValue : theme.radius, style: .continuous) - .strokeBorder(Color.onSurfaceHighEmphasis.opacity(0.15), lineWidth: 2) + .strokeBorder(Color.onSurfacePrimary.opacity(0.15), lineWidth: 2) .opacity(isBordered || theme.borderButtons ? 1 : 0) } } @@ -100,10 +100,10 @@ public struct OversizeButtonStyle: ButtonStyle { switch type { case .primary: switch role { - case .some(.destructive), .some(.cancel): Color.onPrimaryHighEmphasis + case .some(.destructive), .some(.cancel): Color.onPrimary default: if isAccent { - Color.onPrimaryHighEmphasis + Color.onPrimary } else { Color.backgroundPrimary } @@ -116,18 +116,18 @@ public struct OversizeButtonStyle: ButtonStyle { if isAccent { Color.accent } else { - Color.onSurfaceHighEmphasis + Color.onSurfacePrimary } } case .tertiary: switch role { case .some(.destructive): Color.error - case .some(.cancel): Color.onSurfaceHighEmphasis + case .some(.cancel): Color.onSurfacePrimary default: if isAccent { Color.accent } else { - Color.onSurfaceHighEmphasis + Color.onSurfacePrimary } } } diff --git a/Sources/OversizeUI/Controls/Button/FieldButtonStyle.swift b/Sources/OversizeUI/Controls/Button/FieldButtonStyle.swift index 5121cbc..2af80b3 100644 --- a/Sources/OversizeUI/Controls/Button/FieldButtonStyle.swift +++ b/Sources/OversizeUI/Controls/Button/FieldButtonStyle.swift @@ -7,7 +7,7 @@ import SwiftUI public struct FieldButtonStyle: ButtonStyle { @Environment(\.theme) private var theme: ThemeSettings - @Environment(\.fieldPosition) private var fieldPosition: FieldPosition + @Environment(\.fieldPosition) private var fieldPosition: VerticalAlignment? public init() {} @@ -24,16 +24,6 @@ public struct FieldButtonStyle: ButtonStyle { @ViewBuilder private func fieldBackground(isPressed: Bool) -> some View { switch fieldPosition { - case .default: - RoundedRectangle(cornerRadius: Radius.medium, style: .continuous) - .fill(isPressed ? Color.surfaceTertiary : Color.surfaceSecondary) - .overlay( - RoundedRectangle(cornerRadius: Radius.medium, - style: .continuous) - .stroke(theme.borderTextFields - ? Color.border - : Color.surfaceSecondary, lineWidth: CGFloat(theme.borderSize)) - ) case .top, .bottom, .center: #if os(iOS) RoundedRectangleCorner(radius: Radius.medium, corners: backgroundShapeCorners) @@ -55,6 +45,17 @@ public struct FieldButtonStyle: ButtonStyle { : Color.surfaceSecondary, lineWidth: CGFloat(theme.borderSize)) ) #endif + + default: + RoundedRectangle(cornerRadius: Radius.medium, style: .continuous) + .fill(isPressed ? Color.surfaceTertiary : Color.surfaceSecondary) + .overlay( + RoundedRectangle(cornerRadius: Radius.medium, + style: .continuous) + .stroke(theme.borderTextFields + ? Color.border + : Color.surfaceSecondary, lineWidth: CGFloat(theme.borderSize)) + ) } } @@ -64,14 +65,14 @@ public struct FieldButtonStyle: ButtonStyle { @available(tvOS, unavailable) private var backgroundShapeCorners: UIRectCorner { switch fieldPosition { - case .default: - [.allCorners] case .top: [.topLeft, .topRight] case .bottom: [.bottomLeft, .bottomRight] case .center: [] + default: + [.allCorners] } } #endif diff --git a/Sources/OversizeUI/Controls/Checkbox/Checkbox.swift b/Sources/OversizeUI/Controls/Checkbox/Checkbox.swift index 1683531..2a77b9d 100644 --- a/Sources/OversizeUI/Controls/Checkbox/Checkbox.swift +++ b/Sources/OversizeUI/Controls/Checkbox/Checkbox.swift @@ -5,7 +5,7 @@ import SwiftUI -public enum CheckboxAlignment { +public enum CheckboxAlignment: Sendable { case leading, trailing } @@ -77,7 +77,7 @@ public struct Checkbox: View { private func checkboxImage(isEnabled: Bool, isOn: Bool) -> some View { ZStack { RoundedRectangle(cornerRadius: Radius.small, style: .continuous) - .strokeBorder(Color.onSurfaceDisabled.opacity(isEnabled ? 1 : 0.5), lineWidth: 2.5) + .strokeBorder(Color.onSurfaceTertiary.opacity(isEnabled ? 1 : 0.5), lineWidth: 2.5) .frame(width: 24, height: 24) .opacity(isOn ? 0 : 1) @@ -87,16 +87,16 @@ public struct Checkbox: View { Image(systemName: "checkmark") .font(.caption.weight(.black)) - .foregroundColor(.onPrimaryHighEmphasis.opacity(isEnabled ? 1 : 0.5)) + .foregroundColor(.onPrimary.opacity(isEnabled ? 1 : 0.5)) .opacity(isOn ? 1 : 0) } } private var foregroundColor: Color { if isEnabled { - Color.onSurfaceHighEmphasis + Color.onSurfacePrimary } else { - Color.onSurfaceDisabled + Color.onSurfaceTertiary } } } @@ -115,16 +115,18 @@ public extension Checkbox where Label == EmptyView { } } -struct Checkbox_LibraryContent: LibraryContentProvider { - var views: [LibraryItem] { - LibraryItem( - Checkbox(isOn: .constant(false), label: { - Text("Text") - }), - title: "Checkbox", category: .control - ) - } -} +/* + struct Checkbox_LibraryContent: LibraryContentProvider { + var views: [LibraryItem] { + LibraryItem( + Checkbox(isOn: .constant(false), label: { + Text("Text") + }), + title: "Checkbox", category: .control + ) + } + } + */ struct Checkbox_preview: PreviewProvider { static var previews: some View { diff --git a/Sources/OversizeUI/Controls/ColorSelector/ColorSelectorStyle.swift b/Sources/OversizeUI/Controls/ColorSelector/ColorSelectorStyle.swift index 03f792a..42ae91a 100644 --- a/Sources/OversizeUI/Controls/ColorSelector/ColorSelectorStyle.swift +++ b/Sources/OversizeUI/Controls/ColorSelector/ColorSelectorStyle.swift @@ -5,14 +5,14 @@ import SwiftUI -public protocol ColorSelectorStyle { +public protocol ColorSelectorStyle: Sendable { associatedtype Body: View typealias Configuration = ColorSelectorConfiguration func makeBody(configuration: Self.Configuration) -> Self.Body } -public struct ColorSelectorConfiguration { +public struct ColorSelectorConfiguration: Sendable { public struct Label: View { public init(content: some View) { body = AnyView(content) @@ -31,7 +31,7 @@ public struct DefaultColorSelectorStyle: ColorSelectorStyle { } struct ColorSelectorStyleStyleKey: EnvironmentKey { - public static var defaultValue = AnyColorSelectorStyle(style: DefaultColorSelectorStyle()) + public static let defaultValue = AnyColorSelectorStyle(style: DefaultColorSelectorStyle()) } public extension EnvironmentValues { diff --git a/Sources/OversizeUI/Controls/ColorSelector/Styles/AnyColorSelectorStyle.swift b/Sources/OversizeUI/Controls/ColorSelector/Styles/AnyColorSelectorStyle.swift index 2ce0164..e99bffd 100644 --- a/Sources/OversizeUI/Controls/ColorSelector/Styles/AnyColorSelectorStyle.swift +++ b/Sources/OversizeUI/Controls/ColorSelector/Styles/AnyColorSelectorStyle.swift @@ -6,7 +6,7 @@ import SwiftUI public struct AnyColorSelectorStyle: ColorSelectorStyle { - private var _makeBody: (Configuration) -> AnyView + private var _makeBody: @Sendable (Configuration) -> AnyView public init(style: some ColorSelectorStyle) { _makeBody = { configuration in diff --git a/Sources/OversizeUI/Controls/DateField/DateField.swift b/Sources/OversizeUI/Controls/DateField/DateField.swift index 0e6092e..2033480 100644 --- a/Sources/OversizeUI/Controls/DateField/DateField.swift +++ b/Sources/OversizeUI/Controls/DateField/DateField.swift @@ -46,7 +46,7 @@ public struct DateField: View { HStack { Text(label) .subheadline(.medium) - .foregroundColor(.onSurfaceHighEmphasis) + .foregroundColor(.onSurfacePrimary) Spacer() } } @@ -58,7 +58,7 @@ public struct DateField: View { Text(label) .font(.subheadline) .fontWeight(.semibold) - .onSurfaceDisabledForegroundColor() + .onSurfaceTertiaryForeground() } HStack { diff --git a/Sources/OversizeUI/Controls/GridSelect/GridSelect.swift b/Sources/OversizeUI/Controls/GridSelect/GridSelect.swift index 966af35..44a0d86 100644 --- a/Sources/OversizeUI/Controls/GridSelect/GridSelect.swift +++ b/Sources/OversizeUI/Controls/GridSelect/GridSelect.swift @@ -122,7 +122,7 @@ public struct GridSelect: View RoundedRectangle(cornerRadius: radius, style: .continuous) - .strokeBorder(Color.onSurfaceMediumEmphasis, lineWidth: 2) + .strokeBorder(Color.onSurfaceSecondary, lineWidth: 2) case .accentSurface: RoundedRectangle(cornerRadius: radius.rawValue, style: .continuous) @@ -163,7 +163,7 @@ public struct GridSelect: View Circle() .foregroundColor(Color.surfacePrimary) .shadowElevaton(.z2) - IconDeprecated(.checkMini, color: .onSurfaceHighEmphasis) + IconDeprecated(.checkMini, color: .onSurfacePrimary) }.frame(width: Space.large.rawValue, height: Space.large.rawValue) .padding(.small) } diff --git a/Sources/OversizeUI/Controls/GridSelect/GridSelectStyle.swift b/Sources/OversizeUI/Controls/GridSelect/GridSelectStyle.swift index 0023816..d097c13 100644 --- a/Sources/OversizeUI/Controls/GridSelect/GridSelectStyle.swift +++ b/Sources/OversizeUI/Controls/GridSelect/GridSelectStyle.swift @@ -57,24 +57,24 @@ public struct SelectionOnlyGridSelectStyle: GridSelectStyle { // MARK: - Support -public enum GridSelectSeletionIconStyle { +public enum GridSelectSeletionIconStyle: Sendable { case none case checkbox(alignment: Alignment = .bottomTrailing) case radio(alignment: Alignment = .bottomTrailing) } -public enum GridSelectSeletionStyle { +public enum GridSelectSeletionStyle: Sendable { case shadowSurface case graySurface case accentSurface } -public enum GridSelectUnseletionStyle { +public enum GridSelectUnseletionStyle: Sendable { case clean case surface } -public protocol GridSelectStyle { +public protocol GridSelectStyle: Sendable { associatedtype Body: View typealias Configuration = GridSelectConfiguration @@ -103,7 +103,7 @@ public struct AnyGridSelectStyle: GridSelectStyle { public var unseletionStyle: GridSelectUnseletionStyle public var icon: GridSelectSeletionIconStyle - private var _makeBody: (Configuration) -> AnyView + private var _makeBody: @Sendable (Configuration) -> AnyView public init( seletionStyle: GridSelectSeletionStyle, @@ -125,7 +125,7 @@ public struct AnyGridSelectStyle: GridSelectStyle { } struct GridSelectStyleKey: EnvironmentKey { - public static var defaultValue = AnyGridSelectStyle(seletionStyle: .accentSurface, + public static let defaultValue = AnyGridSelectStyle(seletionStyle: .accentSurface, unseletionStyle: .surface, icon: .none, style: IslandGridSelectStyle()) diff --git a/Sources/OversizeUI/Controls/HUD/HUD.swift b/Sources/OversizeUI/Controls/HUD/HUD.swift index eff6dc4..81e9b21 100644 --- a/Sources/OversizeUI/Controls/HUD/HUD.swift +++ b/Sources/OversizeUI/Controls/HUD/HUD.swift @@ -14,8 +14,12 @@ public struct HUD: View where Title: View, Icon: View { private let isAutoHide: Bool @Binding private var isPresented: Bool + #if os(macOS) + @State private var offset: CGFloat = 200 + #else + @State private var offset: CGFloat = -200 + #endif - @State private var bottomOffset: CGFloat = 0 @State private var opacity: CGFloat = 0 // MARK: Initializers @@ -41,7 +45,12 @@ public struct HUD: View where Title: View, Icon: View { if let text { Text(text) .body(.medium) - .foregroundColor(.onSurfaceHighEmphasis) + #if os(macOS) + .foregroundColor(Color.onPrimary) + #else + .foregroundColor(Color.onSurfacePrimary) + + #endif } else if let title { title @@ -50,26 +59,41 @@ public struct HUD: View where Title: View, Icon: View { .padding(.leading, icon == nil ? .medium : .small) .padding(.trailing, .medium) .padding(.vertical, .xSmall) - .background( - Capsule() - .foregroundColor(Color.surfacePrimary) - .shadowElevaton(.z2) - ) - .padding(.small) - .opacity(opacity) - .offset(y: bottomOffset) - .onChange(of: isPresented, perform: { present in - if present { - presentAnimated() - } else { - dismissAnimated() + #if os(macOS) + .background( + RoundedRectangle(cornerRadius: .small, style: .continuous) + .foregroundColor(Color.onBackgroundPrimary) + .shadowElevaton(.z2) + ) + #else + .background( + Capsule() + .foregroundColor(Color.surfacePrimary) + .shadowElevaton(.z2) + ) + #endif + .padding(.small) + .opacity(opacity) + .offset(y: offset) + .onChange(of: isPresented) { present in + if present { + if offset == 0 { + dismissAnimated() + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { + presentAnimated() + } + } else { + presentAnimated() + } + } else { + dismissAnimated() + } } - }) } private func presentAnimated() { withAnimation { - bottomOffset = 0 + offset = 0 opacity = 1 } if isAutoHide { @@ -83,7 +107,11 @@ public struct HUD: View where Title: View, Icon: View { private func dismissAnimated() { withAnimation { - bottomOffset = -200 + #if os(macOS) + offset = 200 + #else + offset = -200 + #endif opacity = 0 } } @@ -132,10 +160,46 @@ public extension HUD where Icon == EmptyView { } } +#if os(macOS) +@MainActor +public extension View { + func hud(_ text: String, autoHide: Bool = true, isPresented: Binding) -> some View { + overlay(alignment: .bottomTrailing) { + HUD(text, autoHide: autoHide, isPresented: isPresented) + } + } + + func hud(_ text: String, isPresented: Binding, @ViewBuilder icon: () -> some View) -> some View { + overlay(alignment: .bottomTrailing) { + HUD(text, isPresented: isPresented, icon: icon) + } + } + + func hud(isPresented: Binding, @ViewBuilder title: () -> some View) -> some View { + overlay(alignment: .bottomTrailing) { + HUD(isPresented: isPresented, title: title) + } + } + + func hud(isPresented: Binding, @ViewBuilder title: () -> some View, @ViewBuilder icon: () -> some View) -> some View { + overlay(alignment: .bottomTrailing) { + HUD(isPresented: isPresented, title: title, icon: icon) + } + } + + func hudLoader(_ text: String = "Loading", isPresented: Binding) -> some View { + overlay(alignment: .bottomTrailing) { + HUD(text, autoHide: false, isPresented: isPresented) { + ProgressView() + } + } + } +} +#else public extension View { - func hud(_ text: String, isPresented: Binding) -> some View { + func hud(_ text: String, autoHide: Bool = true, isPresented: Binding) -> some View { overlay(alignment: .top) { - HUD(text, isPresented: isPresented) + HUD(text, autoHide: autoHide, isPresented: isPresented) } } @@ -165,3 +229,4 @@ public extension View { } } } +#endif diff --git a/Sources/OversizeUI/Controls/IconPicker/IconPicker.swift b/Sources/OversizeUI/Controls/IconPicker/IconPicker.swift index dd1ca6d..ba5562d 100644 --- a/Sources/OversizeUI/Controls/IconPicker/IconPicker.swift +++ b/Sources/OversizeUI/Controls/IconPicker/IconPicker.swift @@ -5,9 +5,7 @@ import SwiftUI -@available(macOS, unavailable) -@available(watchOS, unavailable) -@available(tvOS, unavailable) +#if os(iOS) public struct IconPicker: View { @Environment(\.theme) private var theme: ThemeSettings @Environment(\.horizontalSizeClass) var horizontalSizeClass @@ -47,13 +45,13 @@ public struct IconPicker: View { } label: { HStack(spacing: .xxSmall) { Text(label) - .onSurfaceHighEmphasisForegroundColor() + .onSurfacePrimaryForeground() Spacer() if let image = selection { image } - IconDeprecated(.chevronDown, color: .onSurfaceHighEmphasis) + IconDeprecated(.chevronDown, color: .onSurfacePrimary) } } .buttonStyle(.field) @@ -109,3 +107,4 @@ public struct IconPicker: View { } } } +#endif diff --git a/Sources/OversizeUI/Controls/Label/MenuLabelStyle.swift b/Sources/OversizeUI/Controls/Label/MenuLabelStyle.swift index 123b34b..1d10c74 100644 --- a/Sources/OversizeUI/Controls/Label/MenuLabelStyle.swift +++ b/Sources/OversizeUI/Controls/Label/MenuLabelStyle.swift @@ -19,17 +19,17 @@ public struct MenuLabelStyle: LabelStyle { HStack(spacing: .xSmall) { configuration.icon .headline() - .foregroundColor(.onSurfaceHighEmphasis) + .foregroundColor(.onSurfacePrimary) VStack(alignment: textAlignment, spacing: .xxxSmall) { configuration.title .headline(.semibold) - .foregroundColor(.onSurfaceHighEmphasis) + .foregroundColor(.onSurfacePrimary) if let subtitle, !subtitle.isEmpty { Text(subtitle) .subheadline() - .foregroundColor(.onSurfaceMediumEmphasis) + .foregroundColor(.onSurfaceSecondary) } } } diff --git a/Sources/OversizeUI/Controls/Label/RowLabelStyle.swift b/Sources/OversizeUI/Controls/Label/RowLabelStyle.swift index 03d808f..7f43f24 100644 --- a/Sources/OversizeUI/Controls/Label/RowLabelStyle.swift +++ b/Sources/OversizeUI/Controls/Label/RowLabelStyle.swift @@ -19,17 +19,17 @@ public struct RowLabelStyle: LabelStyle { HStack(spacing: .xSmall) { configuration.icon .headline() - .foregroundColor(.onSurfaceHighEmphasis) + .foregroundColor(.onSurfacePrimary) VStack(alignment: textAlignment, spacing: .xxxSmall) { configuration.title .headline(.semibold) - .foregroundColor(.onSurfaceHighEmphasis) + .foregroundColor(.onSurfacePrimary) if let subtitle, !subtitle.isEmpty { Text(subtitle) .subheadline() - .foregroundColor(.onSurfaceMediumEmphasis) + .foregroundColor(.onSurfaceSecondary) } } } diff --git a/Sources/OversizeUI/Controls/Loader/LoaderOverlayView.swift b/Sources/OversizeUI/Controls/Loader/LoaderOverlayView.swift index 2d09d66..155bc71 100644 --- a/Sources/OversizeUI/Controls/Loader/LoaderOverlayView.swift +++ b/Sources/OversizeUI/Controls/Loader/LoaderOverlayView.swift @@ -59,7 +59,7 @@ public struct LoaderOverlayView: View { if showText { Text(text.isEmpty ? "Loading" : text) .headline(.semibold) - .onSurfaceDisabledForegroundColor() + .onSurfaceTertiaryForeground() .offset(y: 8) } } @@ -74,7 +74,7 @@ public struct LoaderOverlayView: View { if showText { Text(text.isEmpty ? "Loading" : text) .headline(.semibold) - .onSurfaceDisabledForegroundColor() + .onSurfaceTertiaryForeground() .offset(y: 8) } } diff --git a/Sources/OversizeUI/Controls/Menu/BarButtonMenuStyle.swift b/Sources/OversizeUI/Controls/Menu/BarButtonMenuStyle.swift index d755346..1b6a4fe 100644 --- a/Sources/OversizeUI/Controls/Menu/BarButtonMenuStyle.swift +++ b/Sources/OversizeUI/Controls/Menu/BarButtonMenuStyle.swift @@ -17,7 +17,7 @@ public struct BarButtonMenuStyle: MenuStyle { public func makeBody(configuration: Configuration) -> some View { Menu(configuration) .body(true) - .foregroundColor(Color.onPrimaryHighEmphasis) + .foregroundColor(Color.onPrimary) .padding(.xxSmall) .background(background) .shadowElevaton(.z2) @@ -28,7 +28,7 @@ public struct BarButtonMenuStyle: MenuStyle { .fill(Color.surfacePrimary) .overlay { Capsule() - .strokeBorder(Color.onSurfaceHighEmphasis.opacity(0.15), lineWidth: 2) + .strokeBorder(Color.onSurfacePrimary.opacity(0.15), lineWidth: 2) .opacity(isBordered || theme.borderButtons ? 1 : 0) } } diff --git a/Sources/OversizeUI/Controls/Notice/NoticeView.swift b/Sources/OversizeUI/Controls/Notice/NoticeView.swift index 77bd0cb..71ecae7 100644 --- a/Sources/OversizeUI/Controls/Notice/NoticeView.swift +++ b/Sources/OversizeUI/Controls/Notice/NoticeView.swift @@ -52,7 +52,7 @@ public struct NoticeView: View where A: View { Text(title) .headline(.semibold) - .onSurfaceHighEmphasisForegroundColor() + .onSurfacePrimaryForeground() .multilineTextAlignment(.leading) .padding(.trailing, closeAction != nil ? .medium : .zero) } @@ -60,7 +60,7 @@ public struct NoticeView: View where A: View { subtitle.map { text in Text(text) .body(.medium) - .onSurfaceMediumEmphasisForegroundColor() + .onSurfaceSecondaryForeground() .multilineTextAlignment(.leading) } @@ -83,7 +83,7 @@ public struct NoticeView: View where A: View { Button { closeAction?() } label: { - IconDeprecated(.xMini, color: .onSurfaceHighEmphasis) + IconDeprecated(.xMini, color: .onSurfacePrimary) } .buttonStyle(subtitle != nil ? .tertiary(infinityWidth: false) : .quaternary(infinityWidth: false)) .controlBorderShape(.capsule) diff --git a/Sources/OversizeUI/Controls/PageControl/PageIndexView.swift b/Sources/OversizeUI/Controls/PageControl/PageIndexView.swift index 72d48c3..5fc08ef 100644 --- a/Sources/OversizeUI/Controls/PageControl/PageIndexView.swift +++ b/Sources/OversizeUI/Controls/PageControl/PageIndexView.swift @@ -19,7 +19,11 @@ public struct PageIndexView: View { ForEach(0 ..< maxIndex, id: \.self) { index in Capsule() .fill(index == self.index ? Color.accent : Color.surfaceTertiary) + #if os(iOS) .frame(width: index == self.index ? 28 : 8, height: 8) + #else + .frame(width: index == self.index ? 24 : 6, height: 6) + #endif .animation(.default, value: index) } } diff --git a/Sources/OversizeUI/Controls/PageView/Page.swift b/Sources/OversizeUI/Controls/PageView/Page.swift index 9709c53..ab1f07a 100644 --- a/Sources/OversizeUI/Controls/PageView/Page.swift +++ b/Sources/OversizeUI/Controls/PageView/Page.swift @@ -4,6 +4,8 @@ import SwiftUI public struct Page: View where Content: View, Header: View, LeadingBar: View, TrailingBar: View, TopToolbar: View, TitleLabel: View { + @Environment(\.platform) var platform + public typealias ScrollAction = (_ offset: CGPoint, _ headerVisibleRatio: CGFloat) -> Void private let title: String? @@ -103,7 +105,7 @@ public struct Page 20 ? opacity : 0)) + .foregroundColor(Color.onSurfaceTertiary.opacity(height > 20 ? opacity : 0)) configuration.label .frame(maxWidth: .infinity, alignment: .leading) - .foregroundColor(Color.onSurfaceDisabled.opacity(height > 20 ? opacity : 0)) + .foregroundColor(Color.onSurfaceTertiary.opacity(height > 20 ? opacity : 0)) } .callout(.semibold) @@ -1100,7 +1102,7 @@ struct SeartchTextFieldButtonStyle: ButtonStyle { cornerRadius: .medium, style: .continuous ) - .fill(Color.onSurfaceHighEmphasis.opacity(0.07)) + .fill(Color.onSurfacePrimary.opacity(0.07)) ) .submitLabel(.search) } @@ -1109,7 +1111,7 @@ struct SeartchTextFieldButtonStyle: ButtonStyle { private extension View { @ViewBuilder func prefersNavigationBarHidden() -> some View { - #if os(watchOS) + #if os(watchOS) || os(macOS) self #else if #available(iOS 16.0, macOS 13.0, tvOS 16.0, *) { diff --git a/Sources/OversizeUI/Controls/Premium/PremiumLabel.swift b/Sources/OversizeUI/Controls/Premium/PremiumLabel.swift index bb5a27f..1cde093 100644 --- a/Sources/OversizeUI/Controls/Premium/PremiumLabel.swift +++ b/Sources/OversizeUI/Controls/Premium/PremiumLabel.swift @@ -33,11 +33,11 @@ public struct PremiumLabel: View { if let image { image .renderingMode(.template) - .foregroundColor(isMonohrom ? Color(red: 0.718, green: 0.325, blue: 0.459) : .onPrimaryHighEmphasis) + .foregroundColor(isMonohrom ? Color(red: 0.718, green: 0.325, blue: 0.459) : .onPrimary) } Text(text) .font(.system(size: fontSize, weight: .heavy)) - .foregroundColor(isMonohrom ? Color(red: 0.718, green: 0.325, blue: 0.459) : .onPrimaryHighEmphasis) + .foregroundColor(isMonohrom ? Color(red: 0.718, green: 0.325, blue: 0.459) : .onPrimary) } .padding(.leading, leadingPadding) .padding(.trailing, trailingPadding) @@ -47,7 +47,7 @@ public struct PremiumLabel: View { Group { if isMonohrom { RoundedRectangle(cornerRadius: radius, style: .continuous) - .fill(Color.onPrimaryHighEmphasis) + .fill(Color.onPrimary) } else { RoundedRectangle(cornerRadius: radius, style: .continuous) .fill(LinearGradient(gradient: Gradient(colors: [ diff --git a/Sources/OversizeUI/Controls/PriceField/PriceField.swift b/Sources/OversizeUI/Controls/PriceField/PriceField.swift index 8495391..4f19577 100644 --- a/Sources/OversizeUI/Controls/PriceField/PriceField.swift +++ b/Sources/OversizeUI/Controls/PriceField/PriceField.swift @@ -9,7 +9,7 @@ import SwiftUI import UIKit #endif -@available(macOS 13, iOS 16, tvOS 16, watchOS 9, *) +@available(macOS 14, iOS 16, tvOS 16, watchOS 9, *) public struct PriceField: View { private var formatter: NumberFormatter { let formatter = NumberFormatter() diff --git a/Sources/OversizeUI/Controls/Radio/Radio.swift b/Sources/OversizeUI/Controls/Radio/Radio.swift index 4bc8a4f..ccd7f4a 100644 --- a/Sources/OversizeUI/Controls/Radio/Radio.swift +++ b/Sources/OversizeUI/Controls/Radio/Radio.swift @@ -82,7 +82,7 @@ public struct Radio: View { private func radioImage(isEnabled: Bool, isOn: Bool) -> some View { ZStack { Circle() - .stroke(Color.onSurfaceDisabled.opacity(isEnabled ? 1 : 0.5), lineWidth: 4) + .stroke(Color.onSurfaceTertiary.opacity(isEnabled ? 1 : 0.5), lineWidth: 4) .frame(width: 24, height: 24) .cornerRadius(12) .opacity(isOn ? 0 : 1) @@ -100,9 +100,9 @@ public struct Radio: View { private var foregroundColor: Color { if isEnabled { - Color.onSurfaceHighEmphasis + Color.onSurfacePrimary } else { - Color.onSurfaceDisabled + Color.onSurfaceTertiary } } } @@ -121,16 +121,18 @@ public extension Radio where Label == EmptyView { } } -struct Radio_LibraryContent: LibraryContentProvider { - var views: [LibraryItem] { - LibraryItem( - Radio(isOn: false, alignment: .leading, label: { - Text("Text") - }), - title: "Chip", category: .control - ) - } -} +/* + struct Radio_LibraryContent: LibraryContentProvider { + var views: [LibraryItem] { + LibraryItem( + Radio(isOn: false, alignment: .leading, label: { + Text("Text") + }), + title: "Chip", category: .control + ) + } + } + */ struct Radio_preview: PreviewProvider { struct RadioPreview: View { diff --git a/Sources/OversizeUI/Controls/Radio/RadioPicker.swift b/Sources/OversizeUI/Controls/Radio/RadioPicker.swift index 0663843..d05b5d2 100644 --- a/Sources/OversizeUI/Controls/Radio/RadioPicker.swift +++ b/Sources/OversizeUI/Controls/Radio/RadioPicker.swift @@ -54,16 +54,18 @@ public struct RadioPicker: View where Content: View } } -struct RadioPicker_LibraryContent: LibraryContentProvider { - var views: [LibraryItem] { - LibraryItem( - RadioPicker(["One", "Two"], selection: .constant("One")) { item in - Text(item) - }, - title: "Radio Picker", category: .control - ) - } -} +/* + struct RadioPicker_LibraryContent: LibraryContentProvider { + var views: [LibraryItem] { + LibraryItem( + RadioPicker(["One", "Two"], selection: .constant("One")) { item in + Text(item) + }, + title: "Radio Picker", category: .control + ) + } + } + */ struct RadioPicker_previw: PreviewProvider { struct RadioPickerPreview: View { diff --git a/Sources/OversizeUI/Controls/Row/Row.swift b/Sources/OversizeUI/Controls/Row/Row.swift index 95f0e65..5a175d4 100644 --- a/Sources/OversizeUI/Controls/Row/Row.swift +++ b/Sources/OversizeUI/Controls/Row/Row.swift @@ -26,9 +26,6 @@ public struct Row: View where LeadingLabel: View, T private let leadingLabel: LeadingLabel? private let trailingLabel: TrailingLabel? - private var leadingType: RowLeadingType? - private var trallingType: RowTrailingType? - private let action: (() -> Void)? private var isPremiumOption = false @@ -63,26 +60,6 @@ public struct Row: View where LeadingLabel: View, T leadingRadius = nil } - @available(*, deprecated, message: "Use leading: {} and tralling: {}") - public init(_ title: String, - subtitle: String? = nil, - leadingType: RowLeadingType? = nil, - trallingType: RowTrailingType? = nil, - paddingHorizontal _: Space = .medium, - paddingVertical _: Space = .small, - action: (() -> Void)? = nil) - { - self.title = title - self.subtitle = subtitle - self.leadingType = leadingType - self.trallingType = trallingType - self.action = action - leadingLabel = nil - trailingLabel = nil - leadingSize = nil - leadingRadius = nil - } - public var body: some View { if action != nil { Button { @@ -132,7 +109,7 @@ public struct Row: View where LeadingLabel: View, T if isShowArrowIcon { Image.Base.chevronRight - .icon(.onSurfaceDisabled) + .icon(.onSurfaceTertiary) } } } @@ -160,7 +137,7 @@ public struct Row: View where LeadingLabel: View, T сlearAction?() } label: { ZStack { - IconDeprecated(.xMini, color: .onSurfaceDisabled) + IconDeprecated(.xMini, color: .onSurfaceTertiary) .background( RoundedRectangle(cornerRadius: .small, style: .continuous) .fillSurfaceSecondary() @@ -183,7 +160,7 @@ public struct Row: View where LeadingLabel: View, T if let textColor { textColor } else { - Color.onSurfaceHighEmphasis + Color.onSurfacePrimary } } @@ -191,7 +168,7 @@ public struct Row: View where LeadingLabel: View, T if let textColor { textColor } else { - Color.onSurfaceMediumEmphasis + Color.onSurfaceSecondary } } @@ -246,20 +223,6 @@ public extension Row { control.textColor = color return control } - - @available(*, deprecated, message: "Use leading: {} and tralling: {}") - func rowLeading(_ leading: RowLeadingType?) -> Row { - var control = self - control.leadingType = leading - return control - } - - @available(*, deprecated, message: "Use leading: {} and tralling: {}") - func rowTrailing(_ trailing: RowTrailingType?) -> Row { - var control = self - control.trallingType = trailing - return control - } } public extension View { diff --git a/Sources/OversizeUI/Controls/Row/RowButton.swift b/Sources/OversizeUI/Controls/Row/RowButton.swift index 14cec4f..df27aef 100644 --- a/Sources/OversizeUI/Controls/Row/RowButton.swift +++ b/Sources/OversizeUI/Controls/Row/RowButton.swift @@ -68,6 +68,6 @@ public struct RowButton: View { ? Color.link : style == .delete ? Color.error - : Color.onSurfaceHighEmphasis + : Color.onSurfacePrimary } } diff --git a/Sources/OversizeUI/Controls/Row/RowTitle.swift b/Sources/OversizeUI/Controls/Row/RowTitle.swift index b5d641d..fd40d25 100644 --- a/Sources/OversizeUI/Controls/Row/RowTitle.swift +++ b/Sources/OversizeUI/Controls/Row/RowTitle.swift @@ -44,14 +44,14 @@ public struct RowTitle: View { } label: { Text(title) .font(.title2.bold()) - .foregroundColor(.onSurfaceHighEmphasis) + .foregroundColor(.onSurfacePrimary) } .buttonStyle(.scale) } } else { Text(title) .font(.title2.bold()) - .foregroundColor(.onSurfaceHighEmphasis) + .foregroundColor(.onSurfacePrimary) } } @@ -63,7 +63,7 @@ public struct RowTitle: View { action() } label: { IconDeprecated(.chevronRight) - .iconColor(.onSurfaceMediumEmphasis) + .iconColor(.onSurfaceSecondary) .offset(y: titleButtonPosition == .leading ? 1.5 : 0) } .buttonStyle(.scale) diff --git a/Sources/OversizeUI/Controls/ScrollView/ScrollViewWithStickyHeader.swift b/Sources/OversizeUI/Controls/ScrollView/ScrollViewWithStickyHeader.swift index d66930a..415d37a 100644 --- a/Sources/OversizeUI/Controls/ScrollView/ScrollViewWithStickyHeader.swift +++ b/Sources/OversizeUI/Controls/ScrollView/ScrollViewWithStickyHeader.swift @@ -1,7 +1,7 @@ -//// -//// Copyright © 2023 Alexander Romanov -//// File.swift, created on 26.11.2023 -//// +// +// Copyright © 2023 Alexander Romanov +// ScrollViewHeader.swift, created on 26.11.2023 +// // import SwiftUI diff --git a/Sources/OversizeUI/Controls/SectionView/SectionView.swift b/Sources/OversizeUI/Controls/SectionView/SectionView.swift index 9822275..fe86bbf 100644 --- a/Sources/OversizeUI/Controls/SectionView/SectionView.swift +++ b/Sources/OversizeUI/Controls/SectionView/SectionView.swift @@ -5,20 +5,20 @@ import SwiftUI -public enum SectionViewTitlePosition { +public enum SectionViewTitlePosition: Sendable { case inside, outside } -public enum SectionViewTitleButtonPosition { +public enum SectionViewTitleButtonPosition: Sendable { case leading, trailing } -public enum SectionViewTitleButton { - case arrow(_ action: () -> Void) - case title(_ title: String, _ action: () -> Void) +public enum SectionViewTitleButton: Sendable { + case arrow(_ action: @Sendable () -> Void) + case title(_ title: String, _ action: @Sendable () -> Void) } -public enum SectionViewStyle { +public enum SectionViewStyle: Sendable { case `default`, smallIndent, edgeToEdge } @@ -106,9 +106,9 @@ public struct SectionView: View { private var titleColor: Color { switch titlePosition { case .inside: - .onSurfaceHighEmphasis + .onSurfacePrimary case .outside: - .onBackgroundHighEmphasis + .onBackgroundPrimary } } @@ -141,7 +141,7 @@ public struct SectionView: View { action() } label: { IconDeprecated(.chevronRight) - .iconColor(.onSurfaceMediumEmphasis) + .iconColor(.onSurfaceSecondary) .offset(y: titleButtonPosition == .leading ? 1.5 : 0) } .buttonStyle(.scale) diff --git a/Sources/OversizeUI/Controls/SegmentedControl/SegmentedControl.swift b/Sources/OversizeUI/Controls/SegmentedControl/SegmentedControl.swift index ae428a1..6c32c51 100644 --- a/Sources/OversizeUI/Controls/SegmentedControl/SegmentedControl.swift +++ b/Sources/OversizeUI/Controls/SegmentedControl/SegmentedControl.swift @@ -105,7 +105,7 @@ public struct SegmentedPickerSelector: V content(data[index], selectedIndex == index) .body(.semibold) - .foregroundColor(style.seletionStyle == .accentSurface && selectedIndex == index ? Color.onPrimaryHighEmphasis : Color.onSurfaceHighEmphasis) + .foregroundColor(style.seletionStyle == .accentSurface && selectedIndex == index ? Color.onPrimary : Color.onSurfacePrimary) .multilineTextAlignment(.center) .contentShape(Rectangle()) .frame( @@ -173,7 +173,7 @@ public struct SegmentedPickerSelector: V }, label: { content(data[index], selectedIndex == index) .body(.semibold) - .foregroundColor(style.seletionStyle == .accentSurface && selectedIndex == index ? Color.onPrimaryHighEmphasis : Color.onSurfaceHighEmphasis) + .foregroundColor(style.seletionStyle == .accentSurface && selectedIndex == index ? Color.onPrimary : Color.onSurfacePrimary) .multilineTextAlignment(.center) .padding(.leading, controlPadding.leading) .padding(.trailing, controlPadding.trailing) @@ -244,7 +244,7 @@ public struct SegmentedPickerSelector: V ? controlRadius.rawValue - 4 : controlRadius.rawValue, style: .continuous) - .strokeBorder(Color.onSurfaceMediumEmphasis, lineWidth: 2) + .strokeBorder(Color.onSurfaceSecondary, lineWidth: 2) } case .accentSurface: RoundedRectangle( diff --git a/Sources/OversizeUI/Controls/SegmentedControl/SegmentedControlStyle.swift b/Sources/OversizeUI/Controls/SegmentedControl/SegmentedControlStyle.swift index 29286b9..7d634db 100644 --- a/Sources/OversizeUI/Controls/SegmentedControl/SegmentedControlStyle.swift +++ b/Sources/OversizeUI/Controls/SegmentedControl/SegmentedControlStyle.swift @@ -7,7 +7,7 @@ import SwiftUI // MARK: - Styles -public enum SegmentedControlStyleType { +public enum SegmentedControlStyleType: Sendable { case `default` case island(selected: SegmentedControlSeletionStyle = .shadowSurface) case islandLeading(selected: SegmentedControlSeletionStyle = .shadowSurface) @@ -17,13 +17,13 @@ public enum SegmentedControlStyleType { case onlySelectionScroll(selected: SegmentedControlSeletionStyle = .shadowSurface) } -public enum SegmentedControlSeletionStyle { +public enum SegmentedControlSeletionStyle: Sendable { case shadowSurface case graySurface case accentSurface } -public enum SegmentedControlUnseletionStyle { +public enum SegmentedControlUnseletionStyle: Sendable { case clean case surface } @@ -35,66 +35,94 @@ public extension View { case .default: let style: RectangleSegmentedControlStyle = .init() - return environment(\.segmentedControlStyle, - AnySegmentedControlStyle(isEquallySpacing: style.isEquallySpacing, - isShowBackground: style.isShowBackground, - seletionStyle: style.seletionStyle, - unseletionStyle: style.unseletionStyle, - style: style)) + return environment( + \.segmentedControlStyle, + AnySegmentedControlStyle( + isEquallySpacing: style.isEquallySpacing, + isShowBackground: style.isShowBackground, + seletionStyle: style.seletionStyle, + unseletionStyle: style.unseletionStyle, + style: style + ) + ) case let .island(selected: selected): let style: IslandSegmentedControlStyle = .init() - return environment(\.segmentedControlStyle, - AnySegmentedControlStyle(isEquallySpacing: true, - isShowBackground: false, - seletionStyle: selected, - unseletionStyle: .surface, - style: style)) + return environment( + \.segmentedControlStyle, + AnySegmentedControlStyle( + isEquallySpacing: true, + isShowBackground: false, + seletionStyle: selected, + unseletionStyle: .surface, + style: style + ) + ) case let .islandScroll(selected: selected): let style: ScrollSegmentedControlStyle = .init() - return environment(\.segmentedControlStyle, - AnySegmentedControlStyle(isEquallySpacing: false, - isShowBackground: false, - seletionStyle: selected, - unseletionStyle: .surface, - style: style)) + return environment( + \.segmentedControlStyle, + AnySegmentedControlStyle( + isEquallySpacing: false, + isShowBackground: false, + seletionStyle: selected, + unseletionStyle: .surface, + style: style + ) + ) case let .onlySelection(selected: selected): let style: SelectionOnlySegmentedControlStyle = .init() - return environment(\.segmentedControlStyle, - AnySegmentedControlStyle(isEquallySpacing: true, - isShowBackground: false, - seletionStyle: selected, - unseletionStyle: .clean, - style: style)) + return environment( + \.segmentedControlStyle, + AnySegmentedControlStyle( + isEquallySpacing: true, + isShowBackground: false, + seletionStyle: selected, + unseletionStyle: .clean, + style: style + ) + ) case let .onlySelectionLeading(selected: selected): let style: SelectionOnlySegmentedControlStyle = .init() - return environment(\.segmentedControlStyle, - AnySegmentedControlStyle(isEquallySpacing: false, - isShowBackground: false, - seletionStyle: selected, - unseletionStyle: .clean, - style: style)) + return environment( + \.segmentedControlStyle, + AnySegmentedControlStyle( + isEquallySpacing: false, + isShowBackground: false, + seletionStyle: selected, + unseletionStyle: .clean, + style: style + ) + ) case let .onlySelectionScroll(selected: selected): let style: ScrollSegmentedControlStyle = .init() - return environment(\.segmentedControlStyle, - AnySegmentedControlStyle(isEquallySpacing: false, - isShowBackground: false, - seletionStyle: selected, - unseletionStyle: .clean, - style: style)) + return environment( + \.segmentedControlStyle, + AnySegmentedControlStyle( + isEquallySpacing: false, + isShowBackground: false, + seletionStyle: selected, + unseletionStyle: .clean, + style: style + ) + ) case let .islandLeading(selected: selected): let style: IslandSegmentedControlStyle = .init() - return environment(\.segmentedControlStyle, - AnySegmentedControlStyle(isEquallySpacing: false, - isShowBackground: false, - seletionStyle: selected, - unseletionStyle: .surface, - style: style)) + return environment( + \.segmentedControlStyle, + AnySegmentedControlStyle( + isEquallySpacing: false, + isShowBackground: false, + seletionStyle: selected, + unseletionStyle: .surface, + style: style + ) + ) } } } @@ -187,7 +215,7 @@ public struct IslandSegmentedControlStyle: SegmentedControlStyle { // MARK: - Support -public protocol SegmentedControlStyle { +public protocol SegmentedControlStyle: Sendable { associatedtype Body: View typealias Configuration = SegmentedControlConfiguration @@ -199,7 +227,7 @@ public protocol SegmentedControlStyle { func makeBody(configuration: Self.Configuration) -> Self.Body } -public struct SegmentedControlConfiguration { +public struct SegmentedControlConfiguration: Sendable { public struct Label: View { public init(content: some View) { body = AnyView(content) @@ -211,7 +239,7 @@ public struct SegmentedControlConfiguration { public let label: SegmentedControlConfiguration.Label } -public struct AnySegmentedControlStyle: SegmentedControlStyle { +public struct AnySegmentedControlStyle: SegmentedControlStyle, Sendable { public var isEquallySpacing: Bool public var isShowBackground: Bool @@ -220,7 +248,7 @@ public struct AnySegmentedControlStyle: SegmentedControlStyle { public var unseletionStyle: SegmentedControlUnseletionStyle - private var _makeBody: (Configuration) -> AnyView + private var _makeBody: @Sendable (Configuration) -> AnyView public init(isEquallySpacing: Bool, isShowBackground: Bool, @@ -243,11 +271,13 @@ public struct AnySegmentedControlStyle: SegmentedControlStyle { } public struct SegmentedControlKey: EnvironmentKey { - public static var defaultValue = AnySegmentedControlStyle(isEquallySpacing: true, - isShowBackground: true, - seletionStyle: .shadowSurface, - unseletionStyle: .clean, - style: RectangleSegmentedControlStyle()) + public static let defaultValue = AnySegmentedControlStyle( + isEquallySpacing: true, + isShowBackground: true, + seletionStyle: .shadowSurface, + unseletionStyle: .clean, + style: RectangleSegmentedControlStyle() + ) } public extension EnvironmentValues { @@ -259,11 +289,15 @@ public extension EnvironmentValues { public extension View { func segmentedControlStyle(_ style: some SegmentedControlStyle) -> some View { - environment(\.segmentedControlStyle, - AnySegmentedControlStyle(isEquallySpacing: style.isEquallySpacing, - isShowBackground: style.isShowBackground, - seletionStyle: style.seletionStyle, - unseletionStyle: style.unseletionStyle, - style: style)) + environment( + \.segmentedControlStyle, + AnySegmentedControlStyle( + isEquallySpacing: style.isEquallySpacing, + isShowBackground: style.isShowBackground, + seletionStyle: style.seletionStyle, + unseletionStyle: style.unseletionStyle, + style: style + ) + ) } } diff --git a/Sources/OversizeUI/Controls/SegmentedControl/Support/SegmentedControl+Extensions.swift b/Sources/OversizeUI/Controls/SegmentedControl/Support/SegmentedControl+Extensions.swift index 286d5f5..5210817 100644 --- a/Sources/OversizeUI/Controls/SegmentedControl/Support/SegmentedControl+Extensions.swift +++ b/Sources/OversizeUI/Controls/SegmentedControl/Support/SegmentedControl+Extensions.swift @@ -21,7 +21,7 @@ extension View { @ViewBuilder @inlinable func alignmentGuide(_ alignment: HorizontalAlignment, isActive: Bool, - computeValue: @escaping (ViewDimensions) -> CGFloat) -> some View + computeValue: @Sendable @escaping (ViewDimensions) -> CGFloat) -> some View { if isActive { alignmentGuide(alignment, computeValue: computeValue) @@ -33,7 +33,7 @@ extension View { @ViewBuilder @inlinable func alignmentGuide(_ alignment: VerticalAlignment, isActive: Bool, - computeValue: @escaping (ViewDimensions) -> CGFloat) -> some View + computeValue: @Sendable @escaping (ViewDimensions) -> CGFloat) -> some View { if isActive { alignmentGuide(alignment, computeValue: computeValue) diff --git a/Sources/OversizeUI/Controls/SegmentedControl/Support/SegmentedControlPreview.swift b/Sources/OversizeUI/Controls/SegmentedControl/Support/SegmentedControlPreview.swift index fbff8d0..2e65ac9 100644 --- a/Sources/OversizeUI/Controls/SegmentedControl/Support/SegmentedControlPreview.swift +++ b/Sources/OversizeUI/Controls/SegmentedControl/Support/SegmentedControlPreview.swift @@ -71,7 +71,7 @@ struct SegmentedControlPreview: View { Text(item) Text("Subtitle") .subheadline() - .onSurfaceMediumEmphasisForegroundColor() + .onSurfaceSecondaryForeground() } } .segmentedControlStyle(SelectionOnlySegmentedControlStyle()) diff --git a/Sources/OversizeUI/Controls/Select/MultiSelect.swift b/Sources/OversizeUI/Controls/Select/MultiSelect.swift index e9ae58f..a63fd76 100644 --- a/Sources/OversizeUI/Controls/Select/MultiSelect.swift +++ b/Sources/OversizeUI/Controls/Select/MultiSelect.swift @@ -6,6 +6,7 @@ import SwiftUI // swiftlint:disable all +@available(iOS 15.0, macOS 14, tvOS 15.0, watchOS 9.0, *) public struct MultiSelect: View where Content: View, @@ -62,7 +63,7 @@ public struct MultiSelect: View where Content: View, @@ -62,7 +63,7 @@ public struct Select: View { + private let spacing: Space + private let content: Content + + public init(spacing: Space = .zero, @ViewBuilder content: () -> Content) { + self.spacing = spacing + self.content = content() + } + + public var body: some View { + VStack(alignment: .leading, spacing: spacing.rawValue) { + content + } + } +} + +public struct TrailingVStack: View { + private let spacing: Space + private let content: Content + + public init(spacing: Space = .zero, @ViewBuilder content: () -> Content) { + self.spacing = spacing + self.content = content() + } + + public var body: some View { + VStack(alignment: .trailing, spacing: spacing.rawValue) { + content + } + } +} + +public struct CenterVStack: View { + private let spacing: Space + private let content: Content + + public init(spacing: Space = .zero, @ViewBuilder content: () -> Content) { + self.spacing = spacing + self.content = content() + } + + public var body: some View { + VStack(alignment: .center, spacing: spacing.rawValue) { + content + } + } +} + +public struct LeadingLazyVStack: View { + private let spacing: Space + private let content: Content + + public init(spacing: Space = .zero, @ViewBuilder content: () -> Content) { + self.spacing = spacing + self.content = content() + } + + public var body: some View { + LazyVStack(alignment: .leading, spacing: spacing.rawValue) { + content + } + } +} + +public struct TrailingLazyVStack: View { + private let spacing: Space + private let content: Content + + public init(spacing: Space = .zero, @ViewBuilder content: () -> Content) { + self.spacing = spacing + self.content = content() + } + + public var body: some View { + LazyVStack(alignment: .trailing, spacing: spacing.rawValue) { + content + } + } +} + +public struct CenterLazyVStack: View { + private let spacing: Space + private let content: Content + + public init(spacing: Space = .zero, @ViewBuilder content: () -> Content) { + self.spacing = spacing + self.content = content() + } + + public var body: some View { + LazyVStack(alignment: .center, spacing: spacing.rawValue) { + content + } + } +} diff --git a/Sources/OversizeUI/Controls/Surface/Surface.swift b/Sources/OversizeUI/Controls/Surface/Surface.swift index cd13ba9..3b4982e 100644 --- a/Sources/OversizeUI/Controls/Surface/Surface.swift +++ b/Sources/OversizeUI/Controls/Surface/Surface.swift @@ -36,6 +36,8 @@ public struct Surface: View { private let forceContentInsets: EdgeSpaceInsets? private var isSurfaceClipped: Bool = false + @State var isHover = false + public init( action: (() -> Void)? = nil, @ViewBuilder label: () -> Label @@ -58,8 +60,14 @@ public struct Surface: View { action?() } label: { surface + .contentShape(Rectangle()) } .buttonStyle(SurfaceButtonStyle()) + #if os(macOS) + .onHover { hover in + isHover = hover + } + #endif } private var surface: some View { @@ -68,14 +76,33 @@ public struct Surface: View { .padding(.bottom, forceContentInsets?.bottom ?? contentInsets.bottom) .padding(.leading, forceContentInsets?.leading ?? contentInsets.leading) .padding(.trailing, forceContentInsets?.trailing ?? contentInsets.trailing) - .background( + .background { + #if os(macOS) + ZStack { + RoundedRectangle( + cornerRadius: surfaceRadius, + style: .continuous + ) + .fill(surfaceBackgroundColor) + .shadowElevaton(elevation) + + if isHover { + RoundedRectangle( + cornerRadius: surfaceRadius, + style: .continuous + ) + .fill(Color.onSurfaceTertiary.opacity(0.04)) + } + } + #else RoundedRectangle( cornerRadius: surfaceRadius, style: .continuous ) .fill(surfaceBackgroundColor) .shadowElevaton(elevation) - ) + #endif + } .overlay( RoundedRectangle( cornerRadius: surfaceRadius, @@ -171,7 +198,11 @@ public struct SurfaceButtonStyle: ButtonStyle { public func makeBody(configuration: Self.Configuration) -> some View { configuration.label + #if os(macOS) + .scaleEffect(configuration.isPressed ? 0.99 : 1) + #else .scaleEffect(configuration.isPressed ? 0.96 : 1) + #endif } } @@ -252,7 +283,7 @@ struct Surface_Previews: PreviewProvider { static var previews: some View { VStack { Surface { - RowDeprecated("Title") {} + Row("Title") {} } .surfaceStyle(.primary) .previewLayout(.fixed(width: 414, height: 300)) @@ -260,7 +291,7 @@ struct Surface_Previews: PreviewProvider { Surface { Text("Text") .title3() - .onSurfaceHighEmphasisForegroundColor() + .onSurfacePrimaryForeground() } .surfaceStyle(.secondary) .previewLayout(.fixed(width: 414, height: 200)) @@ -268,7 +299,7 @@ struct Surface_Previews: PreviewProvider { Surface { Text("Text") .title3() - .onSurfaceHighEmphasisForegroundColor() + .onSurfacePrimaryForeground() } .surfaceStyle(.primary) .surfaceBorderColor(.surfaceSecondary) diff --git a/Sources/OversizeUI/Controls/Switch/Switch.swift b/Sources/OversizeUI/Controls/Switch/Switch.swift index dac048d..6bf280f 100644 --- a/Sources/OversizeUI/Controls/Switch/Switch.swift +++ b/Sources/OversizeUI/Controls/Switch/Switch.swift @@ -72,7 +72,7 @@ public struct Switch: View { if let subtitle, !subtitle.isEmpty { Text(subtitle) .subheadline() - .foregroundColor(.onSurfaceMediumEmphasis) + .foregroundColor(.onSurfaceSecondary) } } .padding(contentInset) @@ -88,9 +88,9 @@ public struct Switch: View { private var foregroundColor: Color { if isEnabled { - Color.onSurfaceHighEmphasis + Color.onSurfacePrimary } else { - Color.onSurfaceDisabled + Color.onSurfaceTertiary } } } @@ -110,17 +110,19 @@ public extension Switch where Label == EmptyView { } } -@available(iOS 14.0, *) -struct Switch_LibraryContent: LibraryContentProvider { - var views: [LibraryItem] { - LibraryItem( - Switch(isOn: .constant(false)) { - Text("Text") - }, - title: "Checkbox", category: .control - ) - } -} +/* + @available(iOS 14.0, *) + struct Switch_LibraryContent: LibraryContentProvider { + var views: [LibraryItem] { + LibraryItem( + Switch(isOn: .constant(false)) { + Text("Text") + }, + title: "Checkbox", category: .control + ) + } + } + */ struct Switch_preview: PreviewProvider { static var previews: some View { diff --git a/Sources/OversizeUI/Controls/TextBox/TextBox.swift b/Sources/OversizeUI/Controls/TextBox/TextBox.swift index 77c0fb7..9c9cb26 100644 --- a/Sources/OversizeUI/Controls/TextBox/TextBox.swift +++ b/Sources/OversizeUI/Controls/TextBox/TextBox.swift @@ -31,12 +31,14 @@ public struct TextBox: View { public var body: some View { VStack(alignment: textStackAlignment, spacing: textSpacing) { - titleText + Text(title) + .font(titleFont) + .onSurfacePrimaryForeground() - if let subtitle { - Text(subtitle) - .body(.medium) - .foregroundColor(.onSurfaceMediumEmphasis) + subtitle.map { + Text($0) + .font(subtitleFont) + .onSurfaceSecondaryForeground() } } .multilineTextAlignment(multilineTextAlignment) @@ -68,21 +70,26 @@ public struct TextBox: View { } } - private var titleText: some View { - Group { - switch size { - case .small: - Text(title) - .headline(.semibold) - case .medium: - Text(title) - .title2(true) - case .large: - Text(title) - .title(true) - } + private var titleFont: Font { + switch size { + case .small: + .headline.weight(.semibold) + + case .medium: + .title2.weight(.bold) + + case .large: + .title.weight(.semibold) + } + } + + private var subtitleFont: Font { + switch size { + case .small: + .callout.weight(.regular) + default: + .body.weight(.medium) } - .onSurfaceHighEmphasisForegroundColor() } public func textBoxSize(_ size: TextBoxSize) -> TextBox { diff --git a/Sources/OversizeUI/Controls/TextEditor/TextEditorPlaceholderViewModifier.swift b/Sources/OversizeUI/Controls/TextEditor/TextEditorPlaceholderViewModifier.swift index 8a6f9f0..36df967 100644 --- a/Sources/OversizeUI/Controls/TextEditor/TextEditorPlaceholderViewModifier.swift +++ b/Sources/OversizeUI/Controls/TextEditor/TextEditorPlaceholderViewModifier.swift @@ -25,20 +25,18 @@ public struct TextEditorPlaceholderViewModifier: ViewModifier { HStack { Text(placeholder) .subheadline(.medium) - .foregroundColor(.onSurfaceHighEmphasis) + .foregroundColor(.onSurfacePrimary) Spacer() } } content - .padding(.horizontal, .xSmall) - .padding(.top, topInputPadding) - .padding(.bottom, 10) + .padding(padding) .headline(.medium) - .onSurfaceHighEmphasisForegroundColor() + .onSurfacePrimaryForeground() .background { ZStack { - RoundedRectangle(cornerRadius: .medium, style: .continuous) + RoundedRectangle(cornerRadius: fieldRadius, style: .continuous) .fill(isFocused ? Color.surfacePrimary : Color.surfaceSecondary) overlay } @@ -53,12 +51,66 @@ public struct TextEditorPlaceholderViewModifier: ViewModifier { } } - var topInputPadding: CGFloat { + private var fieldRadius: Radius { + #if os(macOS) + return .xSmall + #else + return .medium + #endif + } + + var padding: EdgeInsets { + switch fieldPlaceholderPosition { + case .default, .adjacent: + #if os(macOS) + return .init( + top: 10, + leading: Space.xSmall.rawValue, + bottom: 10, + trailing: Space.xSmall.rawValue + ) + #else + return .init( + top: 10, + leading: Space.xSmall.rawValue, + bottom: 10, + trailing: Space.xSmall.rawValue + ) + #endif + case .overInput: + #if os(macOS) + return .init( + top: text.isEmpty ? 13 : 22, + leading: Space.xxSmall.rawValue, + bottom: 10, + trailing: Space.xxSmall.rawValue + ) + #else + return .init( + top: text.isEmpty ? 8 : 22, + leading: Space.xSmall.rawValue, + bottom: 10, + trailing: Space.xSmall.rawValue + ) + + #endif + } + } + + var labelPadding: EdgeInsets { switch fieldPlaceholderPosition { case .default, .adjacent: - 10 + #if os(macOS) + return .init(Space.xSmall) + #else + return .init(Space.xSmall) + #endif case .overInput: - text.isEmpty ? 8 : 22 + #if os(macOS) + return .init(horizontal: Space.xSmall, vertical: Space.xSmall) + #else + return .init(Space.xxSmall) + #endif } } @@ -69,9 +121,9 @@ public struct TextEditorPlaceholderViewModifier: ViewModifier { if text.isEmpty { Text(placeholder) .subheadline() - .onSurfaceDisabledForegroundColor() + .onSurfaceTertiaryForeground() .opacity(0.7) - .padding(.small) + .padding(labelPadding) } case .adjacent: EmptyView() @@ -79,16 +131,16 @@ public struct TextEditorPlaceholderViewModifier: ViewModifier { Text(placeholder) .font(text.isEmpty ? .headline : .subheadline) .fontWeight(text.isEmpty ? .medium : .semibold) - .onSurfaceDisabledForegroundColor() + .onSurfaceTertiaryForeground() .opacity(0.7) - .padding(.small) + .padding(labelPadding) .offset(y: text.isEmpty ? 0 : -6) } } @ViewBuilder var overlay: some View { - RoundedRectangle(cornerRadius: Radius.medium, style: .continuous) + RoundedRectangle(cornerRadius: fieldRadius, style: .continuous) .stroke(overlayBorderColor, lineWidth: isFocused ? 2 : CGFloat(theme.borderSize)) } @@ -123,10 +175,12 @@ struct TextEditor_preview: PreviewProvider { TextEditor(text: .constant("")) .textEditorPlaceholder("Complaint", text: .constant("")) .fieldLabelPosition(.overInput) + .fieldPosition(.top) TextEditor(text: .constant("Text")) .textEditorPlaceholder("Complaint", text: .constant("Text")) .fieldLabelPosition(.overInput) + .fieldPosition(.bottom) Spacer() } diff --git a/Sources/OversizeUI/Controls/TextField/FieldHelperViewModifier.swift b/Sources/OversizeUI/Controls/TextField/FieldHelperViewModifier.swift index fa1edce..25d640f 100644 --- a/Sources/OversizeUI/Controls/TextField/FieldHelperViewModifier.swift +++ b/Sources/OversizeUI/Controls/TextField/FieldHelperViewModifier.swift @@ -23,13 +23,13 @@ public struct FieldHelperViewModifier: ViewModifier { } public func body(content: Content) -> some View { - VStack(alignment: .leading, spacing: platform == .mac ? .xxxSmall : .xSmall) { + VStack(alignment: .leading, spacing: platform == .macOS ? .xxxSmall : .xSmall) { content if helperText.isEmpty == false, helperStyle != .none { Text(helperText) .subheadline(.medium) .foregroundColor(helperForegroundColor) - .offset(x: platform == .mac ? 4 : 0) + .offset(x: platform == .macOS ? 4 : 0) } } .animation(.easeIn(duration: 0.15), value: helperStyle) @@ -38,7 +38,7 @@ public struct FieldHelperViewModifier: ViewModifier { private var helperForegroundColor: Color { switch helperStyle { case .helperText: - Color.onSurfaceMediumEmphasis + Color.onSurfaceSecondary case .errorText: Color.error case .sussesText: diff --git a/Sources/OversizeUI/Controls/TextField/FieldTitleViewModifier.swift b/Sources/OversizeUI/Controls/TextField/FieldTitleViewModifier.swift index 123fe08..f9323fd 100644 --- a/Sources/OversizeUI/Controls/TextField/FieldTitleViewModifier.swift +++ b/Sources/OversizeUI/Controls/TextField/FieldTitleViewModifier.swift @@ -16,7 +16,7 @@ public struct FieldTitleViewModifier: ViewModifier { VStack(alignment: .leading, spacing: .xSmall) { Text(title) .subheadline(.medium) - .onSurfaceHighEmphasisForegroundColor() + .onSurfacePrimaryForeground() content } diff --git a/Sources/OversizeUI/Controls/TextField/LabeledTextFieldStyle.swift b/Sources/OversizeUI/Controls/TextField/LabeledTextFieldStyle.swift index a88d016..09444cf 100644 --- a/Sources/OversizeUI/Controls/TextField/LabeledTextFieldStyle.swift +++ b/Sources/OversizeUI/Controls/TextField/LabeledTextFieldStyle.swift @@ -6,10 +6,11 @@ import SwiftUI // swiftlint:disable identifier_name +@available(iOS 15.0, macOS 14, tvOS 15.0, watchOS 9.0, *) public struct LabeledTextFieldStyle: TextFieldStyle { @Environment(\.theme) private var theme: ThemeSettings @Environment(\.fieldLabelPosition) private var fieldPlaceholderPosition: FieldLabelPosition - @Environment(\.fieldPosition) private var fieldPosition: FieldPosition + @Environment(\.fieldPosition) private var fieldPosition: VerticalAlignment? @Environment(\.platform) private var platform: Platform @FocusState private var isFocused: Bool @Binding private var text: String @@ -21,19 +22,19 @@ public struct LabeledTextFieldStyle: TextFieldStyle { } public func _body(configuration: TextField) -> some View { - VStack(alignment: .leading, spacing: platform == .mac ? .xxxSmall : .xSmall) { + VStack(alignment: .leading, spacing: platform == .macOS ? .xxxSmall : .xSmall) { if fieldPlaceholderPosition == .adjacent { Text(placeholder) .subheadline(.medium) - .foregroundColor(platform == .mac ? .onSurfaceMediumEmphasis : .onSurfaceHighEmphasis) + .foregroundColor(platform == .macOS ? .onSurfaceSecondary : .onSurfacePrimary) .frame(maxWidth: .infinity, alignment: .leading) - .offset(x: platform == .mac ? 4 : 0) + .offset(x: platform == .macOS ? 4 : 0) } ZStack(alignment: .leading) { labelTextView configuration .headline(.medium) - .foregroundColor(.onSurfaceHighEmphasis) + .foregroundColor(.onSurfacePrimary) .padding(padding) .offset(y: fieldOffset) .focused($isFocused) @@ -62,7 +63,7 @@ public struct LabeledTextFieldStyle: TextFieldStyle { #endif case .overInput: #if os(macOS) - return .init(horizontal: .xxSmall, vertical: .xSmall) + return .init(.xSmall) #else return .init( top: Space.xxxSmall.rawValue + Space.small.rawValue, @@ -83,27 +84,28 @@ public struct LabeledTextFieldStyle: TextFieldStyle { @ViewBuilder private var fieldBackground: some View { switch fieldPosition { - case .default: + case .top, .bottom, .center: #if canImport(UIKit) - RoundedRectangle( - cornerRadius: fieldRadius, - style: .continuous + RoundedRectangleCorner( + radius: fieldRadius, + corners: backgroundShapeCorners ) .fill(isFocused ? Color.surfacePrimary : Color.surfaceSecondary) #else if fieldPlaceholderPosition != .adjacent { - RoundedRectangle( - cornerRadius: fieldRadius, - style: .continuous + RoundedRectangleCorner( + radius: fieldRadius, + corners: backgroundShapeCorners ) .fill(isFocused ? Color.surfacePrimary : Color.surfaceSecondary) } #endif - case .top, .bottom, .center: + + default: #if canImport(UIKit) - RoundedRectangleCorner( - radius: fieldRadius, - corners: backgroundShapeCorners + RoundedRectangle( + cornerRadius: fieldRadius, + style: .continuous ) .fill(isFocused ? Color.surfacePrimary : Color.surfaceSecondary) #else @@ -118,7 +120,7 @@ public struct LabeledTextFieldStyle: TextFieldStyle { } } - var fieldRadius: Radius { + private var fieldRadius: Radius { #if os(macOS) return .xSmall #else @@ -129,14 +131,29 @@ public struct LabeledTextFieldStyle: TextFieldStyle { #if canImport(UIKit) private var backgroundShapeCorners: UIRectCorner { switch fieldPosition { - case .default: + case .top: + [.topLeft, .topRight] + case .bottom: + [.bottomLeft, .bottomRight] + case .center: + [] + default: [.allCorners] + } + } + #endif + + #if canImport(AppKit) + private var backgroundShapeCorners: RectCorner { + switch fieldPosition { case .top: [.topLeft, .topRight] case .bottom: [.bottomLeft, .bottomRight] case .center: [] + default: + [.allCorners] } } #endif @@ -147,7 +164,7 @@ public struct LabeledTextFieldStyle: TextFieldStyle { .zero case .overInput: #if os(macOS) - text.isEmpty ? 0 : 8 + text.isEmpty && !isFocused ? -2 : text.isEmpty ? 0 : isFocused ? 9 : 7 #else text.isEmpty ? 0 : 10 #endif @@ -157,23 +174,6 @@ public struct LabeledTextFieldStyle: TextFieldStyle { @ViewBuilder private var overlay: some View { switch fieldPosition { - case .default: - #if canImport(UIKit) - RoundedRectangle( - cornerRadius: fieldRadius, - style: .continuous - ) - .stroke(overlayBorderColor, lineWidth: isFocused ? 2 : CGFloat(theme.borderSize)) - #else - if fieldPlaceholderPosition != .adjacent { - RoundedRectangle( - cornerRadius: fieldRadius, - style: .continuous - ) - .stroke(overlayBorderColor, lineWidth: isFocused ? 2 : CGFloat(theme.borderSize)) - } - #endif - case .top, .bottom, .center: #if canImport(UIKit) RoundedRectangleCorner(radius: fieldRadius, corners: backgroundShapeCorners) @@ -181,6 +181,22 @@ public struct LabeledTextFieldStyle: TextFieldStyle { overlayBorderColor, lineWidth: isFocused ? 2 : CGFloat(theme.borderSize) ) + #elseif canImport(AppKit) + if fieldPlaceholderPosition != .adjacent { + RoundedRectangleCorner(radius: fieldRadius, corners: backgroundShapeCorners) + .stroke( + overlayBorderColor, + lineWidth: isFocused ? 2 : CGFloat(theme.borderSize) + ) + } + #endif + default: + #if canImport(UIKit) + RoundedRectangle( + cornerRadius: fieldRadius, + style: .continuous + ) + .stroke(overlayBorderColor, lineWidth: isFocused ? 2 : CGFloat(theme.borderSize)) #else if fieldPlaceholderPosition != .adjacent { RoundedRectangle( @@ -200,7 +216,7 @@ public struct LabeledTextFieldStyle: TextFieldStyle { if isFocused { Text(placeholder) .subheadline() - .onSurfaceDisabledForegroundColor() + .onSurfaceTertiaryForeground() .opacity(0.7) #if os(macOS) .padding(.xSmall) @@ -214,7 +230,7 @@ public struct LabeledTextFieldStyle: TextFieldStyle { Text(placeholder) .font(text.isEmpty ? .headline : .subheadline) .fontWeight(text.isEmpty ? .medium : .semibold) - .onSurfaceDisabledForegroundColor() + .onSurfaceTertiaryForeground() #if os(macOS) .padding(.xSmall) .offset(y: text.isEmpty ? 0 : -10) @@ -237,6 +253,7 @@ public struct LabeledTextFieldStyle: TextFieldStyle { } } +@available(iOS 15.0, macOS 14, tvOS 15.0, watchOS 9.0, *) public extension TextFieldStyle where Self == LabeledTextFieldStyle { static var `default`: LabeledTextFieldStyle { LabeledTextFieldStyle(placeholder: "", text: .constant("")) @@ -246,32 +263,3 @@ public extension TextFieldStyle where Self == LabeledTextFieldStyle { LabeledTextFieldStyle(placeholder: placeholder, text: text) } } - -struct LabeledTextFieldStyle_Previews: PreviewProvider { - static var previews: some View { - VStack(spacing: 32) { - TextField("Text", text: .constant("Placeholder")) - .textFieldStyle(.default) - - TextField("Text", text: .constant("Placeholder")) - .textFieldStyle(OverPlaceholderTextFieldStyle(placeholder: "Label")) - - TextField("Text", text: .constant("Placeholder")) - .textFieldStyle(InsidePlaceholderTextFieldStyle(placeholder: "Label")) - - TextField("Text", text: .constant("Placeholder")) - .textFieldStyle(DefaultPlaceholderTextFieldStyle()) - .fieldHelper(.constant("Help"), style: .constant(.helperText)) - - TextField("Text", text: .constant("Placeholder")) - .textFieldStyle(OverPlaceholderTextFieldStyle(placeholder: "Label")) - .fieldHelper(.constant("Ok"), style: .constant(.sussesText)) - - TextField("Text", text: .constant("Placeholder")) - .textFieldStyle(InsidePlaceholderTextFieldStyle(placeholder: "Label")) - .fieldHelper(.constant("Error"), style: .constant(.errorText)) - - }.padding() - .previewLayout(.sizeThatFits) - } -} diff --git a/Sources/OversizeUI/Controls/TextField/TextFieldExtended.swift b/Sources/OversizeUI/Controls/TextField/TextFieldExtended.swift index 152b1e3..e28dc21 100644 --- a/Sources/OversizeUI/Controls/TextField/TextFieldExtended.swift +++ b/Sources/OversizeUI/Controls/TextField/TextFieldExtended.swift @@ -62,7 +62,7 @@ public struct TextFieldExtended: View { HStack { Text(placeholder) .subheadline() - .foregroundColor(.onSurfaceHighEmphasis) + .foregroundColor(.onSurfacePrimary) Spacer() } .padding(.bottom, Space.xxSmall) @@ -81,7 +81,7 @@ public struct TextFieldExtended: View { self.focused = focused }) .headline() - .foregroundColor(.onSurfaceHighEmphasis) + .foregroundColor(.onSurfacePrimary) } if trallingImage != .none { @@ -116,7 +116,7 @@ public struct TextFieldExtended: View { if helperStyle == .helperText { Text(helperText) .subheadline() - .foregroundColor(.onSurfaceMediumEmphasis) + .foregroundColor(.onSurfaceSecondary) } else if helperStyle == .errorText { Text(helperText) .subheadline() diff --git a/Sources/OversizeUI/Controls/Toast/Snackbar.swift b/Sources/OversizeUI/Controls/Toast/Snackbar.swift index ffa46d4..d4df23c 100644 --- a/Sources/OversizeUI/Controls/Toast/Snackbar.swift +++ b/Sources/OversizeUI/Controls/Toast/Snackbar.swift @@ -36,7 +36,7 @@ public struct Snackbar: View where Label: View, Actions: View { if let text { Text(text) .body(.medium) - .foregroundColor(.onPrimaryHighEmphasis) + .foregroundColor(.onPrimary) } else if let label { label @@ -55,7 +55,6 @@ public struct Snackbar: View where Label: View, Actions: View { } } } - .padding(.leading, .medium) .padding(.trailing, .xSmall) .padding(.vertical, .xSmall) diff --git a/Sources/OversizeUI/Controls/Toogle/CheckboxStyle.swift b/Sources/OversizeUI/Controls/Toogle/CheckboxStyle.swift index 5b02752..03c8041 100644 --- a/Sources/OversizeUI/Controls/Toogle/CheckboxStyle.swift +++ b/Sources/OversizeUI/Controls/Toogle/CheckboxStyle.swift @@ -12,13 +12,13 @@ public struct CheckboxStyle: ToggleStyle { HStack { configuration.label .headline(.semibold) - .foregroundColor(.onSurfaceHighEmphasis) + .foregroundColor(.onSurfacePrimary) Spacer() ZStack { RoundedRectangle(cornerRadius: Radius.small, style: .continuous) - .strokeBorder(Color.onSurfaceDisabled, lineWidth: 2.5) + .strokeBorder(Color.onSurfaceTertiary, lineWidth: 2.5) .frame(width: 24, height: 24) .opacity(configuration.isOn ? 0 : 1) @@ -28,7 +28,7 @@ public struct CheckboxStyle: ToggleStyle { Image(systemName: "checkmark") .font(.caption.weight(.black)) - .foregroundColor(.onPrimaryHighEmphasis) + .foregroundColor(.onPrimary) .opacity(configuration.isOn ? 1 : 0) } } diff --git a/Sources/OversizeUI/Controls/Toogle/RadioStyle.swift b/Sources/OversizeUI/Controls/Toogle/RadioStyle.swift index 8d570d5..e602bb6 100644 --- a/Sources/OversizeUI/Controls/Toogle/RadioStyle.swift +++ b/Sources/OversizeUI/Controls/Toogle/RadioStyle.swift @@ -12,13 +12,13 @@ public struct RadioStyle: ToggleStyle { HStack { configuration.label .headline(.semibold) - .foregroundColor(.onSurfaceHighEmphasis) + .foregroundColor(.onSurfacePrimary) Spacer() ZStack { Circle() - .stroke(Color.onSurfaceDisabled, lineWidth: 4) + .stroke(Color.onSurfaceTertiary, lineWidth: 4) .frame(width: 24, height: 24) .cornerRadius(12) .opacity(configuration.isOn ? 0 : 1) diff --git a/Sources/OversizeUI/Controls/URLField/URLField.swift b/Sources/OversizeUI/Controls/URLField/URLField.swift index 341c6de..b0a689a 100644 --- a/Sources/OversizeUI/Controls/URLField/URLField.swift +++ b/Sources/OversizeUI/Controls/URLField/URLField.swift @@ -20,6 +20,7 @@ public struct URLField: View { public init(_ title: String = "URL", url: Binding) { self.title = title _url = url + _urlString = .init(initialValue: url.wrappedValue?.absoluteString ?? "") } public var body: some View { @@ -38,7 +39,7 @@ public struct URLField: View { if state { textFieldHelper = .none - } else if let url = URL(string: urlString), NSWorkspace.shared.urlForApplication(toOpen: url) != nil { + } else if let url = URL(string: urlString) { textFieldHelper = .none self.url = url } else { @@ -56,7 +57,7 @@ public struct URLField: View { textFieldHelper = .errorText } #else - if let url = URL(string: urlString), NSWorkspace.shared.urlForApplication(toOpen: url) != nil { + if let url = URL(string: urlString) { textFieldHelper = .none self.url = url } else { @@ -71,6 +72,11 @@ public struct URLField: View { .textContentType(.URL) .autocorrectionDisabled() .fieldHelper(.constant("Invalid URL"), style: $textFieldHelper) + .onChange(of: url) { newValue in + if let newValue, newValue.absoluteString != urlString { + urlString = newValue.absoluteString + } + } } } #endif diff --git a/Sources/OversizeUI/Core/Appearance/Appearance.swift b/Sources/OversizeUI/Core/Appearance/Appearance.swift index 7eff3b9..10eaed3 100644 --- a/Sources/OversizeUI/Core/Appearance/Appearance.swift +++ b/Sources/OversizeUI/Core/Appearance/Appearance.swift @@ -5,7 +5,7 @@ import SwiftUI -public enum Appearance: Int { +public enum Appearance: Int, Sendable { case system case light case dark @@ -43,5 +43,5 @@ public enum Appearance: Int { } } - public static var allCases: [Appearance] = [.system, .light, .dark] + public static let allCases: [Appearance] = [.system, .light, .dark] } diff --git a/Sources/OversizeUI/Core/Appearance/Theme.swift b/Sources/OversizeUI/Core/Appearance/Theme.swift index 7f58413..96cc35a 100644 --- a/Sources/OversizeUI/Core/Appearance/Theme.swift +++ b/Sources/OversizeUI/Core/Appearance/Theme.swift @@ -6,7 +6,7 @@ import SwiftUI // swiftlint:disable identifier_name -public struct Theme: Identifiable { +public struct Theme: Identifiable, Sendable { public let id: Int public let name: String public let accentColor: Color diff --git a/Sources/OversizeUI/Core/Appearance/ThemeSettings.swift b/Sources/OversizeUI/Core/Appearance/ThemeSettings.swift index 4d12550..2003066 100644 --- a/Sources/OversizeUI/Core/Appearance/ThemeSettings.swift +++ b/Sources/OversizeUI/Core/Appearance/ThemeSettings.swift @@ -5,24 +5,24 @@ import SwiftUI -public enum ThemeSettingsNames { - public static var appearance = "Settings.Appearance" - public static var accentColor = "Settings.AccentColor" - public static var fontTitle = "Settings.FontTitle" - public static var fontParagraph = "Settings.FontParagraph" - public static var fontButton = "Settings.FontButton" - public static var fontOverline = "Settings.FontOverline" - public static var borderApp = "Settings.BorderApp" - public static var borderButtons = "Settings.BorderButtons" - public static var borderSurface = "Settings.BorderSurface" - public static var borderTextFields = "Settings.BorderTextFields" - public static var borderControls = "Settings.BorderControls" - public static var borderSize = "Settings.BorderSize" - public static var theme = "Settings.Theme" - public static var radius = "Settings.Radius" +public enum ThemeSettingsNames: Sendable { + public static let appearance = "Settings.Appearance" + public static let accentColor = "Settings.AccentColor" + public static let fontTitle = "Settings.FontTitle" + public static let fontParagraph = "Settings.FontParagraph" + public static let fontButton = "Settings.FontButton" + public static let fontOverline = "Settings.FontOverline" + public static let borderApp = "Settings.BorderApp" + public static let borderButtons = "Settings.BorderButtons" + public static let borderSurface = "Settings.BorderSurface" + public static let borderTextFields = "Settings.BorderTextFields" + public static let borderControls = "Settings.BorderControls" + public static let borderSize = "Settings.BorderSize" + public static let theme = "Settings.Theme" + public static let radius = "Settings.Radius" } -public class ThemeSettings: ObservableObject { +public class ThemeSettings: ObservableObject, @unchecked Sendable { public init() {} @AppStorage(ThemeSettingsNames.appearance) public var appearance: Appearance = .system @@ -38,13 +38,19 @@ public class ThemeSettings: ObservableObject { @AppStorage(ThemeSettingsNames.borderApp) public var borderApp: Bool = false @AppStorage(ThemeSettingsNames.borderButtons) public var borderButtons: Bool = false + #if os(macOS) + @AppStorage(ThemeSettingsNames.borderSize) public var borderSize: Double = 1 + @AppStorage(ThemeSettingsNames.borderTextFields) public var borderTextFields: Bool = true + @AppStorage(ThemeSettingsNames.borderSurface) public var borderSurface: Bool = true + @AppStorage(ThemeSettingsNames.radius) public var radius: Double = 4 + #else @AppStorage(ThemeSettingsNames.borderSurface) public var borderSurface: Bool = false @AppStorage(ThemeSettingsNames.borderTextFields) public var borderTextFields: Bool = false - @AppStorage(ThemeSettingsNames.borderControls) public var borderControls: Bool = false - @AppStorage(ThemeSettingsNames.borderSize) public var borderSize: Double = 0.5 - @AppStorage(ThemeSettingsNames.radius) public var radius: Double = 8 + #endif + + @AppStorage(ThemeSettingsNames.borderControls) public var borderControls: Bool = false @AppStorage(ThemeSettingsNames.theme) public var theme: Int = 0 diff --git a/Sources/OversizeUI/Core/Colors.swift b/Sources/OversizeUI/Core/Colors.swift index 3a275b5..86e24c5 100644 --- a/Sources/OversizeUI/Core/Colors.swift +++ b/Sources/OversizeUI/Core/Colors.swift @@ -15,17 +15,17 @@ public extension Color { // MARK: - On Primary /// On Primary High Emphasis - static var onPrimaryHighEmphasis: Color { + static var onPrimary: Color { Color("OnPrimaryHighEmphasis", bundle: .module) } /// On Primary Medium Emphasis - static var onPrimaryMediumEmphasis: Color { + static var onPrimarySecondary: Color { Color("OnPrimaryMediumEmphasis", bundle: .module) } /// On Primary Disabled - static var onPrimaryDisabled: Color { + static var onPrimaryTertiary: Color { Color("OnPrimaryDisabled", bundle: .module) } @@ -49,17 +49,17 @@ public extension Color { // MARK: - On Background /// On Background High Emphasis - static var onBackgroundHighEmphasis: Color { + static var onBackgroundPrimary: Color { Color("OnBackgroundHighEmphasis", bundle: .module) } /// On BackgroundMedium Emphasis - static var onBackgroundMediumEmphasis: Color { + static var onBackgroundSecondary: Color { Color("OnBackgroundMediumEmphasis", bundle: .module) } /// On Background Disabled - static var onBackgroundDisabled: Color { + static var onBackgroundTertiary: Color { Color("OnBackgroundDisabled", bundle: .module) } @@ -81,17 +81,17 @@ public extension Color { } /// On Surface High Emphasis - static var onSurfaceHighEmphasis: Color { + static var onSurfacePrimary: Color { Color("OnSurfaceHighEmphasis", bundle: .module) } /// On Surface Medium Emphasis - static var onSurfaceMediumEmphasis: Color { + static var onSurfaceSecondary: Color { Color("OnSurfaceMediumEmphasis", bundle: .module) } /// On Surface Disabled - static var onSurfaceDisabled: Color { + static var onSurfaceTertiary: Color { Color("OnSurfaceDisabled", bundle: .module) } @@ -126,87 +126,87 @@ public extension Color { // MARK: - Foreground Color Extension public extension View { - func onPrimaryHighEmphasisForegroundColor() -> some View { - foregroundColor(Color.onPrimaryHighEmphasis) + nonisolated func onPrimaryForeground() -> some View { + foregroundColor(Color.onPrimary) } - func onPrimaryMediumEmphasisForegroundColor() -> some View { - foregroundColor(Color.onPrimaryMediumEmphasis) + nonisolated func onPrimarySecondaryForeground() -> some View { + foregroundColor(Color.onPrimarySecondary) } - func onPrimaryDisabledForegroundColor() -> some View { - foregroundColor(Color.onPrimaryDisabled) + nonisolated func onPrimaryTertiaryForeground() -> some View { + foregroundColor(Color.onPrimaryTertiary) } - func backgroundPrimaryForegroundColor() -> some View { + nonisolated func backgroundPrimary() -> some View { foregroundColor(Color.backgroundPrimary) } - func backgroundSecondaryForegroundColor() -> some View { + nonisolated func backgroundSecondaryForeground() -> some View { foregroundColor(Color.backgroundSecondary) } - func backgroundTertiaryForegroundColor() -> some View { + nonisolated func backgroundTertiaryForeground() -> some View { foregroundColor(Color.backgroundTertiary) } - func onBackgroundHighEmphasisForegroundColor() -> some View { - foregroundColor(Color.onBackgroundHighEmphasis) + nonisolated func onBackgroundPrimaryForeground() -> some View { + foregroundColor(Color.onBackgroundPrimary) } - func onBackgroundMediumEmphasisForegroundColor() -> some View { - foregroundColor(Color.onBackgroundMediumEmphasis) + nonisolated func onBackgroundSecondaryForeground() -> some View { + foregroundColor(Color.onBackgroundSecondary) } - func onBackgroundDisabledForegroundColor() -> some View { - foregroundColor(Color.onBackgroundDisabled) + nonisolated func onBackgroundTertiaryForeground() -> some View { + foregroundColor(Color.onBackgroundTertiary) } - func surfacePrimaryForegroundColor() -> some View { + nonisolated func surfacePrimaryForeground() -> some View { foregroundColor(Color.surfacePrimary) } - func surfaceSecondaryForegroundColor() -> some View { + nonisolated func surfaceSecondaryForeground() -> some View { foregroundColor(Color.surfaceSecondary) } - func surfaceTertiaryForegroundColor() -> some View { + nonisolated func surfaceTertiaryForeground() -> some View { foregroundColor(Color.surfaceTertiary) } - func onSurfaceHighEmphasisForegroundColor() -> some View { - foregroundColor(Color.onSurfaceHighEmphasis) + nonisolated func onSurfacePrimaryForeground() -> some View { + foregroundColor(Color.onSurfacePrimary) } - func onSurfaceMediumEmphasisForegroundColor() -> some View { - foregroundColor(Color.onSurfaceMediumEmphasis) + nonisolated func onSurfaceSecondaryForeground() -> some View { + foregroundColor(Color.onSurfaceSecondary) } - func onSurfaceDisabledForegroundColor() -> some View { - foregroundColor(Color.onSurfaceDisabled) + nonisolated func onSurfaceTertiaryForeground() -> some View { + foregroundColor(Color.onSurfaceTertiary) } - func accentForegroundColor() -> some View { + nonisolated func accentForeground() -> some View { foregroundColor(Color.accentColor) } - func errorForegroundColor() -> some View { + nonisolated func errorForeground() -> some View { foregroundColor(Color.error) } - func successForegroundColor() -> some View { + nonisolated func successForeground() -> some View { foregroundColor(Color.success) } - func warningForegroundColor() -> some View { + nonisolated func warningForeground() -> some View { foregroundColor(Color.warning) } - func linkForegroundColor() -> some View { + nonisolated func linkForeground() -> some View { foregroundColor(Color.link) } - func borderForegroundColor() -> some View { + nonisolated func borderForeground() -> some View { foregroundColor(Color.border) } } @@ -214,194 +214,194 @@ public extension View { // MARK: - Fill Color Extension public extension Shape { - func fillOnPrimaryHighEmphasis() -> some View { - fill(Color.onPrimaryHighEmphasis) + nonisolated func fillOnPrimary() -> some View { + fill(Color.onPrimary) } - func fillOnPrimaryMediumEmphasis() -> some View { - fill(Color.onPrimaryMediumEmphasis) + nonisolated func fillOnPrimarySecondary() -> some View { + fill(Color.onPrimarySecondary) } - func fillOnPrimaryDisabled() -> some View { - fill(Color.onPrimaryDisabled) + nonisolated func fillOnPrimaryTertiary() -> some View { + fill(Color.onPrimaryTertiary) } - func fillAccent() -> some View { + nonisolated func fillAccent() -> some View { fill(Color.accentColor) } - func fillBackgroundPrimary() -> some View { + nonisolated func fillBackgroundPrimary() -> some View { fill(Color.backgroundPrimary) } - func fillBackgroundSecondary() -> some View { + nonisolated func fillBackgroundSecondary() -> some View { fill(Color.backgroundSecondary) } - func fillBackgroundTertiary() -> some View { + nonisolated func fillBackgroundTertiary() -> some View { fill(Color.backgroundTertiary) } - func fillOnBackgroundHighEmphasis() -> some View { - fill(Color.onBackgroundHighEmphasis) + nonisolated func fillOnBackgroundPrimary() -> some View { + fill(Color.onBackgroundPrimary) } - func fillOnBackgroundMediumEmphasis() -> some View { - fill(Color.onBackgroundMediumEmphasis) + nonisolated func fillOnBackgroundSecondary() -> some View { + fill(Color.onBackgroundSecondary) } - func fillOnBackgroundDisabled() -> some View { - fill(Color.onBackgroundDisabled) + nonisolated func fillOnBackgroundTertiary() -> some View { + fill(Color.onBackgroundTertiary) } - func fillSurfacePrimary() -> some View { + nonisolated func fillSurfacePrimary() -> some View { fill(Color.surfacePrimary) } - func fillSurfaceSecondary() -> some View { + nonisolated func fillSurfaceSecondary() -> some View { fill(Color.surfaceSecondary) } - func fillSurfaceTertiary() -> some View { + nonisolated func fillSurfaceTertiary() -> some View { fill(Color.surfaceTertiary) } - func fillOnSurfaceHighEmphasis() -> some View { - fill(Color.onSurfaceHighEmphasis) + nonisolated func fillOnSurfacePrimary() -> some View { + fill(Color.onSurfacePrimary) } - func fillOnSurfaceMediumEmphasis() -> some View { - fill(Color.onSurfaceMediumEmphasis) + nonisolated func fillOnSurfaceSecondary() -> some View { + fill(Color.onSurfaceSecondary) } - func fillOnSurfaceDisabled() -> some View { - fill(Color.onSurfaceDisabled) + nonisolated func fillOnSurfaceTertiary() -> some View { + fill(Color.onSurfaceTertiary) } - func fillError() -> some View { + nonisolated func fillError() -> some View { fill(Color.error) } - func fillSuccess() -> some View { + nonisolated func fillSuccess() -> some View { fill(Color.success) } - func fillWarning() -> some View { + nonisolated func fillWarning() -> some View { fill(Color.warning) } - func fillLink() -> some View { + nonisolated func fillLink() -> some View { fill(Color.link) } - func fillBorder() -> some View { + nonisolated func fillBorder() -> some View { fill(Color.border) } } public extension View { - @available(*, deprecated, renamed: "onPrimaryHighEmphasisForegroundColor") - func foregroundOnPrimaryHighEmphasis() -> some View { - foregroundColor(Color.onPrimaryHighEmphasis) + @available(*, deprecated, renamed: "onPrimaryForeground") + nonisolated func foregroundOnPrimaryHighEmphasis() -> some View { + foregroundColor(Color.onPrimary) } - @available(*, deprecated, renamed: "onPrimaryMediumEmphasisForegroundColor") - func foregroundOnPrimaryMediumEmphasis() -> some View { - foregroundColor(Color.onPrimaryMediumEmphasis) + @available(*, deprecated, renamed: "onPrimarySecondaryForeground") + nonisolated func foregroundOnPrimaryMediumEmphasis() -> some View { + foregroundColor(Color.onPrimarySecondary) } - @available(*, deprecated, renamed: "onPrimaryDisabledForegroundColor") - func foregroundOnPrimaryDisabled() -> some View { - foregroundColor(Color.onPrimaryDisabled) + @available(*, deprecated, renamed: "onPrimaryTertiaryForeground") + nonisolated func foregroundOnPrimaryDisabled() -> some View { + foregroundColor(Color.onPrimaryTertiary) } - @available(*, deprecated, renamed: "accentForegroundColor") - func foregroundAccent() -> some View { + @available(*, deprecated, renamed: "accentForeground") + nonisolated func foregroundAccent() -> some View { foregroundColor(Color.accentColor) } - @available(*, deprecated, renamed: "backgroundPrimaryForegroundColor") - func foregroundBackgroundPrimary() -> some View { + @available(*, deprecated, renamed: "backgroundPrimary") + nonisolated func foregroundBackgroundPrimary() -> some View { foregroundColor(Color.backgroundPrimary) } - @available(*, deprecated, renamed: "backgroundSecondaryForegroundColor") - func foregroundBackgroundSecondary() -> some View { + @available(*, deprecated, renamed: "backgroundSecondaryForeground") + nonisolated func foregroundBackgroundSecondary() -> some View { foregroundColor(Color.backgroundSecondary) } - @available(*, deprecated, renamed: "backgroundTertiaryForegroundColor") - func foregroundBackgroundTertiary() -> some View { + @available(*, deprecated, renamed: "backgroundTertiaryForeground") + nonisolated func foregroundBackgroundTertiary() -> some View { foregroundColor(Color.backgroundTertiary) } - @available(*, deprecated, renamed: "onBackgroundHighEmphasisForegroundColor") - func foregroundOnBackgroundHighEmphasis() -> some View { - foregroundColor(Color.onBackgroundHighEmphasis) + @available(*, deprecated, renamed: "onBackgroundPrimaryForeground") + nonisolated func foregroundOnBackgroundHighEmphasis() -> some View { + foregroundColor(Color.onBackgroundPrimary) } - @available(*, deprecated, renamed: "onBackgroundMediumEmphasisForegroundColor") - func foregroundOnBackgroundMediumEmphasis() -> some View { - foregroundColor(Color.onBackgroundMediumEmphasis) + @available(*, deprecated, renamed: "onBackgroundSecondaryForeground") + nonisolated func foregroundOnBackgroundMediumEmphasis() -> some View { + foregroundColor(Color.onBackgroundSecondary) } - @available(*, deprecated, renamed: "onBackgroundDisabledForegroundColor") - func foregroundOnBackgroundDisabled() -> some View { - foregroundColor(Color.onBackgroundDisabled) + @available(*, deprecated, renamed: "onBackgroundTertiaryForeground") + nonisolated func foregroundOnBackgroundDisabled() -> some View { + foregroundColor(Color.onBackgroundTertiary) } - @available(*, deprecated, renamed: "surfacePrimaryForegroundColor") - func foregroundSurfacePrimary() -> some View { + @available(*, deprecated, renamed: "surfacePrimaryForeground") + nonisolated func foregroundSurfacePrimary() -> some View { foregroundColor(Color.surfacePrimary) } - @available(*, deprecated, renamed: "surfaceSecondaryForegroundColor") - func foregroundSurfaceSecondary() -> some View { + @available(*, deprecated, renamed: "surfaceSecondaryForeground") + nonisolated func foregroundSurfaceSecondary() -> some View { foregroundColor(Color.surfaceSecondary) } @available(*, deprecated, renamed: "aurfaceTertiaryForegroundColor") - func foregroundSurfaceTertiary() -> some View { + nonisolated func foregroundSurfaceTertiary() -> some View { foregroundColor(Color.surfaceTertiary) } @available(*, deprecated, renamed: "onSurfaceHighEmphasisForegroundColor") - func foregroundOnSurfaceHighEmphasis() -> some View { - foregroundColor(Color.onSurfaceHighEmphasis) + nonisolated func foregroundOnSurfaceHighEmphasis() -> some View { + foregroundColor(Color.onSurfacePrimary) } - @available(*, deprecated, renamed: "onSurfaceMediumEmphasisForegroundColor") - func foregroundOnSurfaceMediumEmphasis() -> some View { - foregroundColor(Color.onSurfaceMediumEmphasis) + @available(*, deprecated, renamed: "onSurfaceSecondaryForeground") + nonisolated func foregroundOnSurfaceMediumEmphasis() -> some View { + foregroundColor(Color.onSurfaceSecondary) } - @available(*, deprecated, renamed: "onSurfaceDisabledForegroundColor") - func foregroundOnSurfaceDisabled() -> some View { - foregroundColor(Color.onSurfaceDisabled) + @available(*, deprecated, renamed: "onSurfaceTertiaryForeground") + nonisolated func foregroundOnSurfaceDisabled() -> some View { + foregroundColor(Color.onSurfaceTertiary) } - @available(*, deprecated, renamed: "errorForegroundColor") - func foregroundError() -> some View { + @available(*, deprecated, renamed: "errorForeground") + nonisolated func foregroundError() -> some View { foregroundColor(Color.error) } - @available(*, deprecated, renamed: "successForegroundColor") - func foregroundSuccess() -> some View { + @available(*, deprecated, renamed: "successForeground") + nonisolated func foregroundSuccess() -> some View { foregroundColor(Color.success) } - @available(*, deprecated, renamed: "warningForegroundColor") - func foregroundWarning() -> some View { + @available(*, deprecated, renamed: "warningForeground") + nonisolated func foregroundWarning() -> some View { foregroundColor(Color.warning) } - @available(*, deprecated, renamed: "linkForegroundColor") - func foregroundLink() -> some View { + @available(*, deprecated, renamed: "linkForeground") + nonisolated func foregroundLink() -> some View { foregroundColor(Color.link) } - @available(*, deprecated, renamed: "borderForegroundColor") - func foregroundBorder() -> some View { + @available(*, deprecated, renamed: "borderForeground") + nonisolated func foregroundBorder() -> some View { foregroundColor(Color.border) } } @@ -410,16 +410,16 @@ struct Color_Previews: PreviewProvider { static var previews: some View { VStack(alignment: .leading, spacing: 10) { Text("Accent") - .foregroundColor(Color.onPrimaryMediumEmphasis) + .foregroundColor(Color.onPrimarySecondary) .padding() HStack { Rectangle().size(CGSize(width: 50, height: 50)) - .foregroundColor(Color.onPrimaryHighEmphasis) + .foregroundColor(Color.onPrimary) Rectangle().size(CGSize(width: 50, height: 50)) - .foregroundColor(Color.onPrimaryMediumEmphasis) + .foregroundColor(Color.onPrimarySecondary) Rectangle().size(CGSize(width: 50, height: 50)) - .foregroundColor(Color.onPrimaryDisabled) + .foregroundColor(Color.onPrimaryTertiary) Rectangle().size(CGSize(width: 50, height: 50)) } .padding(.horizontal) diff --git a/Sources/OversizeUI/Core/EdgeSpaceInsets.swift b/Sources/OversizeUI/Core/EdgeSpaceInsets.swift index f3fcf0b..9426e18 100644 --- a/Sources/OversizeUI/Core/EdgeSpaceInsets.swift +++ b/Sources/OversizeUI/Core/EdgeSpaceInsets.swift @@ -5,7 +5,7 @@ import Foundation -public struct EdgeSpaceInsets { +public struct EdgeSpaceInsets: Sendable { public let top: Space public let leading: Space public let bottom: Space diff --git a/Sources/OversizeUI/Core/Elevation.swift b/Sources/OversizeUI/Core/Elevation.swift index 108eeb4..57db644 100644 --- a/Sources/OversizeUI/Core/Elevation.swift +++ b/Sources/OversizeUI/Core/Elevation.swift @@ -6,7 +6,7 @@ import SwiftUI // swiftlint:disable identifier_name -public enum Elevation: Int, CaseIterable { +public enum Elevation: Int, CaseIterable, Sendable { case z0 case z1 case z2 diff --git a/Sources/OversizeUI/Core/EnvironmentKeys/AccentEnvironment.swift b/Sources/OversizeUI/Core/EnvironmentKeys/AccentEnvironment.swift index 2b3c744..dce7fa4 100644 --- a/Sources/OversizeUI/Core/EnvironmentKeys/AccentEnvironment.swift +++ b/Sources/OversizeUI/Core/EnvironmentKeys/AccentEnvironment.swift @@ -6,7 +6,7 @@ import SwiftUI private struct AccentStateKey: EnvironmentKey { - public static var defaultValue: Bool = false + public static let defaultValue: Bool = false } public extension EnvironmentValues { diff --git a/Sources/OversizeUI/Core/EnvironmentKeys/BorderedEnvironment.swift b/Sources/OversizeUI/Core/EnvironmentKeys/BorderedEnvironment.swift index 5fb0c13..74b6747 100644 --- a/Sources/OversizeUI/Core/EnvironmentKeys/BorderedEnvironment.swift +++ b/Sources/OversizeUI/Core/EnvironmentKeys/BorderedEnvironment.swift @@ -6,7 +6,7 @@ import SwiftUI private struct BorderedStateKey: EnvironmentKey { - public static var defaultValue: Bool = false + public static let defaultValue: Bool = false } public extension EnvironmentValues { diff --git a/Sources/OversizeUI/Core/EnvironmentKeys/ControlBorderShapeEnvironment.swift b/Sources/OversizeUI/Core/EnvironmentKeys/ControlBorderShapeEnvironment.swift index e57e1f7..f8c25b6 100644 --- a/Sources/OversizeUI/Core/EnvironmentKeys/ControlBorderShapeEnvironment.swift +++ b/Sources/OversizeUI/Core/EnvironmentKeys/ControlBorderShapeEnvironment.swift @@ -5,13 +5,13 @@ import SwiftUI -public enum ControlBorderShape { +public enum ControlBorderShape: Sendable { case capsule case roundedRectangle(radius: Radius = .medium) } private struct ControlBorderShapeKey: EnvironmentKey { - public static var defaultValue: ControlBorderShape = .roundedRectangle(radius: .medium) + public static let defaultValue: ControlBorderShape = .roundedRectangle(radius: .medium) } public extension EnvironmentValues { diff --git a/Sources/OversizeUI/Core/EnvironmentKeys/ControlPaddingEnvironment.swift b/Sources/OversizeUI/Core/EnvironmentKeys/ControlPaddingEnvironment.swift index e40107d..5310897 100644 --- a/Sources/OversizeUI/Core/EnvironmentKeys/ControlPaddingEnvironment.swift +++ b/Sources/OversizeUI/Core/EnvironmentKeys/ControlPaddingEnvironment.swift @@ -5,7 +5,7 @@ import SwiftUI -public struct ControlMargin { +public struct ControlMargin: Sendable { public var top: Space = .medium public var leading: Space = .medium public var bottom: Space = .medium @@ -88,7 +88,7 @@ public struct ControlMargin { } private struct ControlMarginKey: EnvironmentKey { - public static var defaultValue: ControlMargin = .init(.medium) + public static let defaultValue: ControlMargin = .init(.medium) } public extension EnvironmentValues { diff --git a/Sources/OversizeUI/Core/EnvironmentKeys/ElevationEnvironment.swift b/Sources/OversizeUI/Core/EnvironmentKeys/ElevationEnvironment.swift index fd65f22..02c2c1d 100644 --- a/Sources/OversizeUI/Core/EnvironmentKeys/ElevationEnvironment.swift +++ b/Sources/OversizeUI/Core/EnvironmentKeys/ElevationEnvironment.swift @@ -6,7 +6,7 @@ import SwiftUI private struct ElevationStateKey: EnvironmentKey { - public static var defaultValue: Elevation = .z0 + public static let defaultValue: Elevation = .z0 } public extension EnvironmentValues { diff --git a/Sources/OversizeUI/Core/EnvironmentKeys/FieldLabelPositionEnvironment.swift b/Sources/OversizeUI/Core/EnvironmentKeys/FieldLabelPositionEnvironment.swift index f937246..e7a198b 100644 --- a/Sources/OversizeUI/Core/EnvironmentKeys/FieldLabelPositionEnvironment.swift +++ b/Sources/OversizeUI/Core/EnvironmentKeys/FieldLabelPositionEnvironment.swift @@ -5,12 +5,12 @@ import SwiftUI -public enum FieldLabelPosition { +public enum FieldLabelPosition: Sendable { case `default`, adjacent, overInput } private struct FieldLabelPositionKey: EnvironmentKey { - public static var defaultValue: FieldLabelPosition = .default + public static let defaultValue: FieldLabelPosition = .default } public extension EnvironmentValues { diff --git a/Sources/OversizeUI/Core/EnvironmentKeys/FieldPositionEnvironment.swift b/Sources/OversizeUI/Core/EnvironmentKeys/FieldPositionEnvironment.swift index 5ea8649..6f38a90 100644 --- a/Sources/OversizeUI/Core/EnvironmentKeys/FieldPositionEnvironment.swift +++ b/Sources/OversizeUI/Core/EnvironmentKeys/FieldPositionEnvironment.swift @@ -5,23 +5,19 @@ import SwiftUI -public enum FieldPosition { - case `default`, top, bottom, center -} - private struct FieldPositionKey: EnvironmentKey { - public static var defaultValue: FieldPosition = .default + public static let defaultValue: VerticalAlignment? = nil } public extension EnvironmentValues { - var fieldPosition: FieldPosition { + var fieldPosition: VerticalAlignment? { get { self[FieldPositionKey.self] } set { self[FieldPositionKey.self] = newValue } } } public extension View { - func fieldPosition(_ position: FieldPosition) -> some View { + func fieldPosition(_ position: VerticalAlignment?) -> some View { environment(\.fieldPosition, position) } } diff --git a/Sources/OversizeUI/Core/EnvironmentKeys/IconStyleEnvironment.swift b/Sources/OversizeUI/Core/EnvironmentKeys/IconStyleEnvironment.swift index 36445e9..3b867fc 100644 --- a/Sources/OversizeUI/Core/EnvironmentKeys/IconStyleEnvironment.swift +++ b/Sources/OversizeUI/Core/EnvironmentKeys/IconStyleEnvironment.swift @@ -5,12 +5,12 @@ import SwiftUI -public enum IconStyle { +public enum IconStyle: Sendable { case line, fill, twoTone } private struct IconStyleKey: EnvironmentKey { - public static var defaultValue: IconStyle = .line + public static let defaultValue: IconStyle = .line } public extension EnvironmentValues { diff --git a/Sources/OversizeUI/Core/EnvironmentKeys/LoadingEnvironment.swift b/Sources/OversizeUI/Core/EnvironmentKeys/LoadingEnvironment.swift index 9d6663b..192685d 100644 --- a/Sources/OversizeUI/Core/EnvironmentKeys/LoadingEnvironment.swift +++ b/Sources/OversizeUI/Core/EnvironmentKeys/LoadingEnvironment.swift @@ -6,7 +6,7 @@ import SwiftUI private struct LoadingStateKey: EnvironmentKey { - public static var defaultValue: Bool = false + public static let defaultValue: Bool = false } public extension EnvironmentValues { diff --git a/Sources/OversizeUI/Core/EnvironmentKeys/MultiSelectStyleEnvironment.swift b/Sources/OversizeUI/Core/EnvironmentKeys/MultiSelectStyleEnvironment.swift index 72e17fa..af58cbc 100644 --- a/Sources/OversizeUI/Core/EnvironmentKeys/MultiSelectStyleEnvironment.swift +++ b/Sources/OversizeUI/Core/EnvironmentKeys/MultiSelectStyleEnvironment.swift @@ -5,12 +5,12 @@ import SwiftUI -public enum MultiSelectStyle { +public enum MultiSelectStyle: Sendable { case `default`, section } private struct MultiSelectStyleKey: EnvironmentKey { - public static var defaultValue: MultiSelectStyle = .default + public static let defaultValue: MultiSelectStyle = .default } public extension EnvironmentValues { diff --git a/Sources/OversizeUI/Core/EnvironmentKeys/Platform.swift b/Sources/OversizeUI/Core/EnvironmentKeys/Platform.swift index 853b23b..911f8a7 100644 --- a/Sources/OversizeUI/Core/EnvironmentKeys/Platform.swift +++ b/Sources/OversizeUI/Core/EnvironmentKeys/Platform.swift @@ -8,36 +8,38 @@ import SwiftUI import UIKit #endif -public enum Platform { - case iPhone, iPad, mac, tv, watch, vision, carPlay, other +public enum Platform: Sendable { + case iPhone, iPadOS, macOS, tvOS, watchOS, visionOS, carPlay, other } private struct PlatformKey: EnvironmentKey { static let defaultValue: Platform = { #if os(macOS) || targetEnvironment(macCatalyst) - return .mac + return .macOS #elseif os(watchOS) - return .watch + return .watchOS #elseif os(visionOS) return .vision #elseif canImport(UIKit) - switch UIDevice.current.userInterfaceIdiom { - case .phone: - return .iPhone - case .pad: - return .iPad - case .tv: - return .tv - case .carPlay: - return .carPlay - case .mac: - return .mac - case .vision: - return .vision - case .unspecified: - return .other - @unknown default: - return .other + MainActor.assumeIsolated { + switch UIDevice.current.userInterfaceIdiom { + case .phone: + return .iPhone + case .pad: + return .iPadOS + case .tv: + return .tvOS + case .carPlay: + return .carPlay + case .mac: + return .macOS + case .vision: + return .visionOS + case .unspecified: + return .other + @unknown default: + return .other + } } #else return .other diff --git a/Sources/OversizeUI/Core/EnvironmentKeys/PortraitEnvironment.swift b/Sources/OversizeUI/Core/EnvironmentKeys/PortraitEnvironment.swift index 5ae6981..caf3f8d 100644 --- a/Sources/OversizeUI/Core/EnvironmentKeys/PortraitEnvironment.swift +++ b/Sources/OversizeUI/Core/EnvironmentKeys/PortraitEnvironment.swift @@ -6,7 +6,7 @@ import SwiftUI private struct PortraitStateKey: EnvironmentKey { - public static var defaultValue: Bool = false + public static let defaultValue: Bool = false } public extension EnvironmentValues { diff --git a/Sources/OversizeUI/Core/EnvironmentKeys/PremiumEnvironment.swift b/Sources/OversizeUI/Core/EnvironmentKeys/PremiumEnvironment.swift index 5e8e041..d89f80a 100644 --- a/Sources/OversizeUI/Core/EnvironmentKeys/PremiumEnvironment.swift +++ b/Sources/OversizeUI/Core/EnvironmentKeys/PremiumEnvironment.swift @@ -6,7 +6,7 @@ import SwiftUI private struct PremiumStateKey: EnvironmentKey { - public static var defaultValue: Bool = false + public static let defaultValue: Bool = false } public extension EnvironmentValues { diff --git a/Sources/OversizeUI/Core/EnvironmentKeys/RadiusEnvironment.swift b/Sources/OversizeUI/Core/EnvironmentKeys/RadiusEnvironment.swift index ba6ba34..37f1541 100644 --- a/Sources/OversizeUI/Core/EnvironmentKeys/RadiusEnvironment.swift +++ b/Sources/OversizeUI/Core/EnvironmentKeys/RadiusEnvironment.swift @@ -6,7 +6,7 @@ import SwiftUI private struct ControlRadiusKey: EnvironmentKey { - public static var defaultValue: Radius = .medium + public static let defaultValue: Radius = .medium } public extension EnvironmentValues { diff --git a/Sources/OversizeUI/Core/EnvironmentKeys/ScreenSizeEnvironment.swift b/Sources/OversizeUI/Core/EnvironmentKeys/ScreenSizeEnvironment.swift index 64430d1..cc86468 100644 --- a/Sources/OversizeUI/Core/EnvironmentKeys/ScreenSizeEnvironment.swift +++ b/Sources/OversizeUI/Core/EnvironmentKeys/ScreenSizeEnvironment.swift @@ -5,7 +5,7 @@ import SwiftUI -public struct ScreenSize { +public struct ScreenSize: Sendable { public let safeAreaWidth: CGFloat public let safeAreaHeight: CGFloat public let safeAreaTop: CGFloat diff --git a/Sources/OversizeUI/Core/EnvironmentKeys/SectionViewStyleEnvironment.swift b/Sources/OversizeUI/Core/EnvironmentKeys/SectionViewStyleEnvironment.swift index b17dfd1..fd3bb93 100644 --- a/Sources/OversizeUI/Core/EnvironmentKeys/SectionViewStyleEnvironment.swift +++ b/Sources/OversizeUI/Core/EnvironmentKeys/SectionViewStyleEnvironment.swift @@ -6,7 +6,7 @@ import SwiftUI private struct SectionViewStyleKey: EnvironmentKey { - public static var defaultValue: SectionViewStyle = .default + public static let defaultValue: SectionViewStyle = .default } public extension EnvironmentValues { diff --git a/Sources/OversizeUI/Core/EnvironmentKeys/SelectPickerStyleEnvironment.swift b/Sources/OversizeUI/Core/EnvironmentKeys/SelectPickerStyleEnvironment.swift index 464cebc..422b5f1 100644 --- a/Sources/OversizeUI/Core/EnvironmentKeys/SelectPickerStyleEnvironment.swift +++ b/Sources/OversizeUI/Core/EnvironmentKeys/SelectPickerStyleEnvironment.swift @@ -5,7 +5,7 @@ import SwiftUI -public enum SelectStyle { +public enum SelectStyle: Sendable { case `default` case section @available(macOS, unavailable) @@ -13,7 +13,7 @@ public enum SelectStyle { } private struct SelectStyleKey: EnvironmentKey { - public static var defaultValue: SelectStyle = .default + public static let defaultValue: SelectStyle = .default } public extension EnvironmentValues { diff --git a/Sources/OversizeUI/Core/EnvironmentKeys/SurfaceElevationEnvironment.swift b/Sources/OversizeUI/Core/EnvironmentKeys/SurfaceElevationEnvironment.swift index 1dae333..24b0810 100644 --- a/Sources/OversizeUI/Core/EnvironmentKeys/SurfaceElevationEnvironment.swift +++ b/Sources/OversizeUI/Core/EnvironmentKeys/SurfaceElevationEnvironment.swift @@ -6,7 +6,7 @@ import SwiftUI private struct SurfaceElevationStateKey: EnvironmentKey { - public static var defaultValue: Elevation = .z0 + public static let defaultValue: Elevation = .z0 } public extension EnvironmentValues { diff --git a/Sources/OversizeUI/Core/EnvironmentKeys/SurfaceRadiusEnvironment.swift b/Sources/OversizeUI/Core/EnvironmentKeys/SurfaceRadiusEnvironment.swift index a34a9af..75c6634 100644 --- a/Sources/OversizeUI/Core/EnvironmentKeys/SurfaceRadiusEnvironment.swift +++ b/Sources/OversizeUI/Core/EnvironmentKeys/SurfaceRadiusEnvironment.swift @@ -6,7 +6,7 @@ import SwiftUI private struct SurfaceRadiusKey: EnvironmentKey { - public static var defaultValue: Radius = .medium + public static let defaultValue: Radius = .medium } public extension EnvironmentValues { diff --git a/Sources/OversizeUI/Core/EnvironmentKeys/ThemeEnvironment.swift b/Sources/OversizeUI/Core/EnvironmentKeys/ThemeEnvironment.swift index 6c11667..f242edf 100644 --- a/Sources/OversizeUI/Core/EnvironmentKeys/ThemeEnvironment.swift +++ b/Sources/OversizeUI/Core/EnvironmentKeys/ThemeEnvironment.swift @@ -6,7 +6,7 @@ import SwiftUI private struct ThemeStateKey: EnvironmentKey { - public static var defaultValue = ThemeSettings() + public static let defaultValue = ThemeSettings() } public extension EnvironmentValues { diff --git a/Sources/OversizeUI/Core/Palette.swift b/Sources/OversizeUI/Core/Palette.swift index 93a5cb9..3f65e28 100644 --- a/Sources/OversizeUI/Core/Palette.swift +++ b/Sources/OversizeUI/Core/Palette.swift @@ -5,7 +5,7 @@ import SwiftUI -public enum Palette: String { +public enum Palette: String, Sendable { case red case orange case yellow @@ -39,8 +39,8 @@ public enum Palette: String { } } - public static var base: [Palette] = [.red, .orange, .yellow, .green, .blue, .pink, .gray, .black] - public static var baseColors: [Color] = base.compactMap { color in + public static let base: [Palette] = [.red, .orange, .yellow, .green, .blue, .pink, .gray, .black] + public static let baseColors: [Color] = base.compactMap { color in color.color } } diff --git a/Sources/OversizeUI/Core/PreferenceKeys/ScrollOffsetPreferenceKey.swift b/Sources/OversizeUI/Core/PreferenceKeys/ScrollOffsetPreferenceKey.swift index 612c37c..c2d296a 100644 --- a/Sources/OversizeUI/Core/PreferenceKeys/ScrollOffsetPreferenceKey.swift +++ b/Sources/OversizeUI/Core/PreferenceKeys/ScrollOffsetPreferenceKey.swift @@ -6,6 +6,6 @@ import SwiftUI public struct ScrollOffsetPreferenceKey: PreferenceKey { - public static var defaultValue: CGPoint = .zero + public static let defaultValue: CGPoint = .zero public static func reduce(value _: inout CGPoint, nextValue _: () -> CGPoint) {} } diff --git a/Sources/OversizeUI/Core/PreferenceKeys/SizePreferenceKey.swift b/Sources/OversizeUI/Core/PreferenceKeys/SizePreferenceKey.swift index b74d21d..05517b9 100644 --- a/Sources/OversizeUI/Core/PreferenceKeys/SizePreferenceKey.swift +++ b/Sources/OversizeUI/Core/PreferenceKeys/SizePreferenceKey.swift @@ -6,6 +6,6 @@ import SwiftUI public struct SizePreferenceKey: PreferenceKey { - public static var defaultValue: CGSize = .zero + public static let defaultValue: CGSize = .zero public static func reduce(value _: inout CGSize, nextValue _: () -> CGSize) {} } diff --git a/Sources/OversizeUI/Core/Radius.swift b/Sources/OversizeUI/Core/Radius.swift index 51d37e3..df7ea16 100644 --- a/Sources/OversizeUI/Core/Radius.swift +++ b/Sources/OversizeUI/Core/Radius.swift @@ -5,7 +5,7 @@ import SwiftUI -public enum Radius { +public enum Radius: Sendable { private var theme: ThemeSettings { ThemeSettings() } diff --git a/Sources/OversizeUI/Core/Space.swift b/Sources/OversizeUI/Core/Space.swift index 4704e29..550f79a 100644 --- a/Sources/OversizeUI/Core/Space.swift +++ b/Sources/OversizeUI/Core/Space.swift @@ -5,7 +5,7 @@ import SwiftUI -public enum Space: CGFloat { +public enum Space: CGFloat, Sendable { /// 0 case zero = 0 diff --git a/Sources/OversizeUI/Core/Typography.swift b/Sources/OversizeUI/Core/Typography.swift index 12a9631..427aa77 100644 --- a/Sources/OversizeUI/Core/Typography.swift +++ b/Sources/OversizeUI/Core/Typography.swift @@ -1,11 +1,6 @@ -// -// Copyright © 2021 Alexander Romanov -// Typography.swift, created on 07.06.2020 -// - import SwiftUI -public enum FontDesignType: String, CaseIterable { +public enum FontDesignType: String, CaseIterable, Sendable { case `default`, serif, round, mono public var system: Font.Design { @@ -24,13 +19,12 @@ public enum FontDesignType: String, CaseIterable { public struct Typography: ViewModifier { @Environment(\.theme) private var theme: ThemeSettings - @Environment(\.isLoading) var isLoading private let fontStyle: Font.TextStyle private let isBold: Bool? private let weight: Font.Weight? - public init(fontStyle: Font.TextStyle, isBold: Bool? = nil, weight: Font.Weight? = nil) { + public nonisolated init(fontStyle: Font.TextStyle, isBold: Bool? = nil, weight: Font.Weight? = nil) { self.fontStyle = fontStyle self.isBold = isBold self.weight = weight @@ -38,168 +32,167 @@ public struct Typography: ViewModifier { public func body(content: Content) -> some View { content - .font(.system(fontStyle, design: fontDesign).weight(fontWeight).leading(.tight)) - .frame(minHeight: lineHeight) + .font( + .system(fontStyle, design: fontDesign) + .weight(fontWeight) + .leading(.tight) + ) .lineSpacing(lineHeight * 0.2) + .frame(minHeight: lineHeight) } + #if os(macOS) private var lineHeight: CGFloat { switch fontStyle { - case .largeTitle: - return 44 - case .title: - return 36 - case .title2: - return 28 - case .title3: - return 24 - case .headline: - return 24 - case .subheadline: - return 20 - case .body: - return 24 - case .callout: - return 20 - case .footnote: - return 16 - case .caption: - return 16 - case .caption2: - return 12 - @unknown default: - return 16 + case .largeTitle: 32 + case .title: 28 + case .title2: 24 + case .title3: 20 + case .headline: 16 + case .subheadline: 20 + case .body, .callout, .footnote, .caption, .caption2: 16 + default: 16 } } + #else + private var lineHeight: CGFloat { + switch fontStyle { + case .largeTitle: 44 + case .title: 36 + case .title2: 28 + case .title3, .headline: 24 + case .subheadline, .body: 20 + case .callout, .footnote, .caption, .caption2: 16 + default: 16 + } + } + #endif private var fontDesign: Font.Design { switch fontStyle { case .largeTitle, .title, .title2, .title3, .headline, .subheadline: - return theme.fontTitle.system + theme.fontTitle.system case .body: - return theme.fontParagraph.system + theme.fontParagraph.system case .caption, .caption2, .footnote, .callout: - return theme.fontOverline.system - @unknown default: - return .default + theme.fontOverline.system + default: + .default } } private var fontWeight: Font.Weight { if let weight { - weight - } else { - switch fontStyle { - case .largeTitle, .title: - isBold ?? true ? .bold : .regular - case .headline: - isBold ?? true ? .bold : .semibold - default: - isBold ?? false ? .bold : .regular - } + return weight + } + switch fontStyle { + case .largeTitle, .title: + return (isBold ?? true) ? .bold : .regular + case .headline: + return (isBold ?? true) ? .bold : .semibold + default: + return (isBold ?? false) ? .bold : .regular } } } public extension View { - func largeTitle(_ bold: Bool = true) -> some View { + // Large Title + nonisolated func largeTitle(_ bold: Bool = true) -> some View { modifier(Typography(fontStyle: .largeTitle, isBold: bold)) } - func title(_ bold: Bool = true) -> some View { - modifier(Typography(fontStyle: .title, isBold: bold)) - } - - func title2(_ bold: Bool = true) -> some View { - modifier(Typography(fontStyle: .title2, isBold: bold)) - } - - func title3(_ bold: Bool = true) -> some View { - modifier(Typography(fontStyle: .title3, isBold: bold)) + nonisolated func largeTitle(_ weight: Font.Weight) -> some View { + modifier(Typography(fontStyle: .largeTitle, weight: weight)) } - func headline(_ bold: Bool = true) -> some View { - modifier(Typography(fontStyle: .headline, isBold: bold)) + // Title + nonisolated func title(_ bold: Bool = true) -> some View { + modifier(Typography(fontStyle: .title, isBold: bold)) } - func subheadline(_ bold: Bool = false) -> some View { - modifier(Typography(fontStyle: .subheadline, isBold: bold)) + nonisolated func title(_ weight: Font.Weight) -> some View { + modifier(Typography(fontStyle: .title, weight: weight)) } - func body(_ bold: Bool = false) -> some View { - modifier(Typography(fontStyle: .body, isBold: bold)) + // Title 2 + nonisolated func title2(_ bold: Bool = true) -> some View { + modifier(Typography(fontStyle: .title2, isBold: bold)) } - func callout(_ bold: Bool = false) -> some View { - modifier(Typography(fontStyle: .callout, isBold: bold)) + nonisolated func title2(_ weight: Font.Weight) -> some View { + modifier(Typography(fontStyle: .title2, weight: weight)) } - func footnote(_ bold: Bool = false) -> some View { - modifier(Typography(fontStyle: .footnote, isBold: bold)) + // Title 3 + nonisolated func title3(_ bold: Bool = true) -> some View { + modifier(Typography(fontStyle: .title3, isBold: bold)) } - func caption(_ bold: Bool = false) -> some View { - modifier(Typography(fontStyle: .caption, isBold: bold)) + nonisolated func title3(_ weight: Font.Weight) -> some View { + modifier(Typography(fontStyle: .title3, weight: weight)) } - func caption2(_ bold: Bool = false) -> some View { - modifier(Typography(fontStyle: .caption2, isBold: bold)) + // Headline + nonisolated func headline(_ bold: Bool = true) -> some View { + modifier(Typography(fontStyle: .headline, isBold: bold)) } - func largeTitle(_ weight: Font.Weight) -> some View { - modifier(Typography(fontStyle: .largeTitle, weight: weight)) + nonisolated func headline(_ weight: Font.Weight) -> some View { + modifier(Typography(fontStyle: .headline, weight: weight)) } - func title(_ weight: Font.Weight) -> some View { - modifier(Typography(fontStyle: .title, weight: weight)) + // Subheadline + nonisolated func subheadline(_ bold: Bool = false) -> some View { + modifier(Typography(fontStyle: .subheadline, isBold: bold)) } - func title2(_ weight: Font.Weight) -> some View { - modifier(Typography(fontStyle: .title2, weight: weight)) + nonisolated func subheadline(_ weight: Font.Weight) -> some View { + modifier(Typography(fontStyle: .subheadline, weight: weight)) } - func title3(_ weight: Font.Weight) -> some View { - modifier(Typography(fontStyle: .title3, weight: weight)) + // Body + nonisolated func body(_ bold: Bool = false) -> some View { + modifier(Typography(fontStyle: .body, isBold: bold)) } - func headline(_ weight: Font.Weight) -> some View { - modifier(Typography(fontStyle: .headline, weight: weight)) + nonisolated func body(_ weight: Font.Weight) -> some View { + modifier(Typography(fontStyle: .body, weight: weight)) } - func subheadline(_ weight: Font.Weight) -> some View { - modifier(Typography(fontStyle: .subheadline, weight: weight)) + // Callout + nonisolated func callout(_ bold: Bool = false) -> some View { + modifier(Typography(fontStyle: .callout, isBold: bold)) } - func body(_ weight: Font.Weight) -> some View { - modifier(Typography(fontStyle: .body, weight: weight)) + nonisolated func callout(_ weight: Font.Weight) -> some View { + modifier(Typography(fontStyle: .callout, weight: weight)) } - func callout(_ weight: Font.Weight) -> some View { - modifier(Typography(fontStyle: .callout, weight: weight)) + // Footnote + nonisolated func footnote(_ bold: Bool = false) -> some View { + modifier(Typography(fontStyle: .footnote, isBold: bold)) } - func footnote(_ weight: Font.Weight) -> some View { + nonisolated func footnote(_ weight: Font.Weight) -> some View { modifier(Typography(fontStyle: .footnote, weight: weight)) } - func caption(_ weight: Font.Weight) -> some View { - modifier(Typography(fontStyle: .caption, weight: weight)) + // Caption + nonisolated func caption(_ bold: Bool = false) -> some View { + modifier(Typography(fontStyle: .caption, isBold: bold)) } - func caption2(_ weight: Font.Weight) -> some View { - modifier(Typography(fontStyle: .caption2, weight: weight)) + nonisolated func caption(_ weight: Font.Weight) -> some View { + modifier(Typography(fontStyle: .caption, weight: weight)) } -} -public extension View { - @available(*, deprecated, message: "Use native modificator", renamed: "font") - func fontStyle(_ style: Font.TextStyle) -> some View { - modifier(Typography(fontStyle: style)) + // Caption 2 + nonisolated func caption2(_ bold: Bool = false) -> some View { + modifier(Typography(fontStyle: .caption2, isBold: bold)) } - @available(*, deprecated, message: "Use native color modificator", renamed: "font") - func fontStyle(_ style: Font.TextStyle, color: Color) -> some View { - modifier(Typography(fontStyle: style)) - .foregroundColor(color) + nonisolated func caption2(_ weight: Font.Weight) -> some View { + modifier(Typography(fontStyle: .caption2, weight: weight)) } } diff --git a/Sources/OversizeUI/Core/Validation.swift b/Sources/OversizeUI/Core/Validation.swift index 93ca1ad..617d812 100644 --- a/Sources/OversizeUI/Core/Validation.swift +++ b/Sources/OversizeUI/Core/Validation.swift @@ -5,7 +5,7 @@ import Foundation -public enum Validation { +public enum Validation: Sendable { case success case failure(message: String) diff --git a/Sources/OversizeUI/Core/ViewModifier/HalfSheet/HalfSheet.swift b/Sources/OversizeUI/Core/ViewModifier/HalfSheet/HalfSheet.swift index 991661a..4c44e26 100644 --- a/Sources/OversizeUI/Core/ViewModifier/HalfSheet/HalfSheet.swift +++ b/Sources/OversizeUI/Core/ViewModifier/HalfSheet/HalfSheet.swift @@ -8,13 +8,14 @@ import SwiftUI import UIKit #endif -public enum Detents: Hashable { +public enum Detents: Hashable, Sendable { case large case medium case height(CGFloat) #if os(iOS) @available(iOS 15, *) + @MainActor public var uiViewDetents: UISheetPresentationController.Detent { switch self { case .large: @@ -44,6 +45,7 @@ public enum Detents: Hashable { #if os(iOS) public struct SheetModifier: ViewModifier { public let detents: [Detents] + public func body(content: Content) -> some View { SheetView(detents: detents) { content diff --git a/Sources/OversizeUI/Deprecated/BlurView.swift b/Sources/OversizeUI/Deprecated/BlurView.swift deleted file mode 100644 index 7eec056..0000000 --- a/Sources/OversizeUI/Deprecated/BlurView.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// Copyright © 2021 Alexander Romanov -// BlurView.swift, created on 11.09.2021 -// - -import Foundation -import SwiftUI - -// swiftlint:disable all -#if os(iOS) -public struct BlurView: UIViewRepresentable { - public init(_ style: UIBlurEffect.Style = .light) { - self.style = style - } - - public let style: UIBlurEffect.Style - - public func makeUIView(context _: UIViewRepresentableContext) -> UIView { - let view: UIView = .init(frame: .zero) - view.backgroundColor = .clear - let blurEffect: UIBlurEffect = .init(style: style) - let blurView: UIVisualEffectView = .init(effect: blurEffect) - blurView.translatesAutoresizingMaskIntoConstraints = false - view.insertSubview(blurView, at: 0) - NSLayoutConstraint.activate([ - blurView.heightAnchor.constraint(equalTo: view.heightAnchor), - blurView.widthAnchor.constraint(equalTo: view.widthAnchor), - ]) - return view - } - - public func updateUIView(_: UIView, - context _: UIViewRepresentableContext) {} -} -#endif diff --git a/Sources/OversizeUI/Deprecated/ButtonLegacy.swift b/Sources/OversizeUI/Deprecated/ButtonLegacy.swift deleted file mode 100644 index de2ee23..0000000 --- a/Sources/OversizeUI/Deprecated/ButtonLegacy.swift +++ /dev/null @@ -1,300 +0,0 @@ -// -// Copyright © 2021 Alexander Romanov -// ButtonLegacy.swift, created on 11.09.2021 -// - -import SwiftUI - -public enum LegacyButtonType: Int, CaseIterable { - case accent - case primary - case secondary - case gray - case text - case link - case deleteLink -} - -public enum ButtonSize { - case large - case medium -} - -public enum ButtonRounded { - case full - case medium - case small - case none -} - -public enum ButtonWidth { - case full - case round - case standart -} - -// swiftlint:disable all -public struct ButtonStyleExtended: ButtonStyle { - @Environment(\.theme) private var theme: ThemeSettings - - var style: LegacyButtonType - var size: ButtonSize = .large - var rounded: ButtonRounded = .medium - var width: ButtonWidth = .full - var shadow: Bool = true - - private enum Constants { - /// Size - static var heightL: CGFloat { Space.xLarge.rawValue + Space.xxSmall.rawValue } - static var heightM: CGFloat { Space.large.rawValue + Space.xxSmall.rawValue } - - /// Radius - static var radiusM: CGFloat { Radius.medium.rawValue } - static var radiusS: CGFloat { Radius.small.rawValue } - } - - @ViewBuilder - public func makeBody(configuration: ButtonStyle.Configuration) -> some View { - switch style { - case .primary: - PrimaryButton(size: size, rounded: rounded, width: width, shadow: shadow, isBordered: theme.borderButtons, borderLineWidth: CGFloat(theme.borderSize), configuration: configuration) - case .secondary: - SecondaryButton(size: size, rounded: rounded, width: width, shadow: shadow, isBordered: theme.borderButtons, borderLineWidth: CGFloat(theme.borderSize), configuration: configuration) - case .gray: - GrayButton(size: size, rounded: rounded, width: width, shadow: shadow, isBordered: theme.borderButtons, borderLineWidth: CGFloat(theme.borderSize), configuration: configuration) - case .text: - TextButton(size: size, rounded: rounded, width: width, configuration: configuration) - case .link: - LinkButton(size: size, rounded: rounded, width: width, shadow: shadow, configuration: configuration) - case .deleteLink: - DeleteButton(size: size, rounded: rounded, width: width, shadow: shadow, configuration: configuration) - case .accent: - AccentButton(size: size, rounded: rounded, width: width, shadow: shadow, configuration: configuration, isBordered: theme.borderButtons, borderLineWidth: CGFloat(theme.borderSize)) - } - } - - struct AccentButton: View { - var size: ButtonSize - var rounded: ButtonRounded - var width: ButtonWidth - var shadow: Bool - let configuration: ButtonStyle.Configuration - var isBordered: Bool - var borderLineWidth: CGFloat - @Environment(\.isEnabled) private var isEnabled: Bool - var body: some View { - configuration.label - .body(true) - .padding(.horizontal, Space.small) - .foregroundColor(isEnabled ? .onPrimaryHighEmphasis : .onPrimaryDisabled) - .frame(maxWidth: width == .full ? .infinity : width == .standart ? nil : size == .large ? Constants.heightL : Constants.heightM, - minHeight: size == .large ? ButtonStyleExtended.Constants.heightL : ButtonStyleExtended.Constants.heightM) - .background( - RoundedRectangle(cornerRadius: rounded == .none ? 0 : rounded == .medium ? ButtonStyleExtended.Constants.radiusM : rounded == .small ? ButtonStyleExtended.Constants.radiusS : - size == .large ? ButtonStyleExtended.Constants.heightL / 2 : ButtonStyleExtended.Constants.heightM / 2, - style: .continuous) - .fill(isEnabled ? Color.accent : Color.accent.opacity(0.9)) - .overlay( - RoundedRectangle(cornerRadius: rounded == .none ? 0 : rounded == .medium ? ButtonStyleExtended.Constants.radiusM : rounded == .small ? ButtonStyleExtended.Constants.radiusS : - size == .large ? ButtonStyleExtended.Constants.heightL / 2 : ButtonStyleExtended.Constants.heightM / 2, - style: .continuous) - .stroke(isBordered - ? Color.border - : isEnabled ? Color.accent : Color.accent.opacity(0.9), lineWidth: 1) - ) - ) - .opacity(configuration.isPressed ? 0.9 : 1) - .scaleEffect(configuration.isPressed ? 0.95 : 1) - // .animation(.spring()) - .shadowElevaton(shadow ? .z2 : .z0, color: Color.accent) - } - } - - struct PrimaryButton: View { - var size: ButtonSize - var rounded: ButtonRounded - var width: ButtonWidth - var shadow: Bool - var isBordered: Bool - var borderLineWidth: CGFloat - let configuration: ButtonStyle.Configuration - @Environment(\.isEnabled) private var isEnabled: Bool - var body: some View { - configuration.label - .body(true) - .padding(.horizontal, Space.small) - .foregroundColor(isEnabled ? Color.backgroundPrimary : .onPrimaryDisabled) - .frame(maxWidth: width == .full ? .infinity : width == .standart ? nil : size == .large ? Constants.heightL : Constants.heightM, - minHeight: size == .large ? ButtonStyleExtended.Constants.heightL : ButtonStyleExtended.Constants.heightM) - .background( - RoundedRectangle(cornerRadius: rounded == .none ? 0 : rounded == .medium ? ButtonStyleExtended.Constants.radiusM : rounded == .small ? ButtonStyleExtended.Constants.radiusS : - size == .large ? ButtonStyleExtended.Constants.heightL / 2 : ButtonStyleExtended.Constants.heightM / 2, - style: .continuous) - .fill(isEnabled ? Color.primary : Color.primary.opacity(0.9)) - .overlay( - RoundedRectangle(cornerRadius: rounded == .none ? 0 : rounded == .medium ? ButtonStyleExtended.Constants.radiusM : rounded == .small ? ButtonStyleExtended.Constants.radiusS : - size == .large ? ButtonStyleExtended.Constants.heightL / 2 : ButtonStyleExtended.Constants.heightM / 2, - style: .continuous) - .stroke(isBordered - ? Color.border - : isEnabled ? Color.primary : Color.primary.opacity(0.9), lineWidth: 1) - ) - ) - .opacity(configuration.isPressed ? 0.9 : 1) - .scaleEffect(configuration.isPressed ? 0.95 : 1) - .shadowElevaton(shadow ? .z2 : .z0, color: Color.primary) - } - } - - struct SecondaryButton: View { - var size: ButtonSize - var rounded: ButtonRounded - var width: ButtonWidth - var shadow: Bool - var isBordered: Bool - var borderLineWidth: CGFloat - let configuration: ButtonStyle.Configuration - @Environment(\.isEnabled) private var isEnabled: Bool - var body: some View { - configuration.label - .body(true) - .padding(.horizontal, Space.small) - .foregroundColor(isEnabled ? .onSurfaceHighEmphasis : .onSurfaceDisabled) - .frame(maxWidth: width == .full ? .infinity : width == .standart ? nil : size == .large ? Constants.heightL : Constants.heightM, - minHeight: size == .large ? ButtonStyleExtended.Constants.heightL : ButtonStyleExtended.Constants.heightM) - .background( - RoundedRectangle(cornerRadius: rounded == .none ? 0 : rounded == .medium ? ButtonStyleExtended.Constants.radiusM : rounded == .small ? ButtonStyleExtended.Constants.radiusS : - size == .large ? ButtonStyleExtended.Constants.heightL / 2 : ButtonStyleExtended.Constants.heightM / 2, - style: .continuous) - .fill(isEnabled ? Color.surfacePrimary : Color.surfaceSecondary.opacity(0.9)) - .overlay( - RoundedRectangle(cornerRadius: rounded == .none ? 0 : rounded == .medium ? ButtonStyleExtended.Constants.radiusM : rounded == .small ? ButtonStyleExtended.Constants.radiusS : - size == .large ? ButtonStyleExtended.Constants.heightL / 2 : ButtonStyleExtended.Constants.heightM / 2, - style: .continuous) - .stroke(isBordered - ? Color.border - : isEnabled ? Color.surfacePrimary : Color.surfaceSecondary.opacity(0.9), lineWidth: 1) - ) - ) - .opacity(configuration.isPressed ? 0.9 : 1) - .scaleEffect(configuration.isPressed ? 0.95 : 1) - .shadowElevaton(shadow ? .z2 : .z0) - } - } - - struct GrayButton: View { - var size: ButtonSize - var rounded: ButtonRounded - var width: ButtonWidth - var shadow: Bool - var isBordered: Bool - var borderLineWidth: CGFloat - let configuration: ButtonStyle.Configuration - @Environment(\.isEnabled) private var isEnabled: Bool - var body: some View { - configuration.label - .body(true) - .padding(.horizontal, Space.small) - .foregroundColor(isEnabled ? .onBackgroundHighEmphasis : .onBackgroundDisabled) - .frame(maxWidth: width == .full ? .infinity : width == .standart ? nil : size == .large ? Constants.heightL : Constants.heightM, - minHeight: size == .large ? ButtonStyleExtended.Constants.heightL : ButtonStyleExtended.Constants.heightM) - .background( - RoundedRectangle(cornerRadius: rounded == .none ? 0 : rounded == .medium ? ButtonStyleExtended.Constants.radiusM : rounded == .small ? ButtonStyleExtended.Constants.radiusS : - size == .large ? ButtonStyleExtended.Constants.heightL / 2 : ButtonStyleExtended.Constants.heightM / 2, - style: .continuous) - .fill(isEnabled ? Color.surfaceSecondary : Color.surfaceSecondary.opacity(0.9)) - .overlay( - RoundedRectangle(cornerRadius: rounded == .none ? 0 : rounded == .medium ? ButtonStyleExtended.Constants.radiusM : rounded == .small ? ButtonStyleExtended.Constants.radiusS : - size == .large ? ButtonStyleExtended.Constants.heightL / 2 : ButtonStyleExtended.Constants.heightM / 2, - style: .continuous) - .stroke(isBordered - ? Color.border - : isEnabled ? Color.surfaceSecondary : Color.surfaceSecondary.opacity(0.9), lineWidth: 1) - ) - ) - .opacity(configuration.isPressed ? 0.9 : 1) - .scaleEffect(configuration.isPressed ? 0.95 : 1) - .shadowElevaton(shadow ? .z2 : .z0) - } - } - - struct TextButton: View { - var size: ButtonSize - var rounded: ButtonRounded - var width: ButtonWidth - let configuration: ButtonStyle.Configuration - @Environment(\.isEnabled) private var isEnabled: Bool - var body: some View { - configuration.label - .body(true) - .padding(.horizontal, Space.small) - .foregroundColor(isEnabled ? .onSurfaceHighEmphasis : .onSurfaceDisabled) - .frame(maxWidth: width == .full ? .infinity : width == .standart ? nil : size == .large ? Constants.heightL : Constants.heightM, - minHeight: size == .large ? ButtonStyleExtended.Constants.heightL : ButtonStyleExtended.Constants.heightM) - .opacity(configuration.isPressed ? 0.9 : 1) - .scaleEffect(configuration.isPressed ? 0.95 : 1) - } - } - - struct LinkButton: View { - var size: ButtonSize - var rounded: ButtonRounded - var width: ButtonWidth - var shadow: Bool - let configuration: ButtonStyle.Configuration - @Environment(\.isEnabled) private var isEnabled: Bool - var body: some View { - configuration.label - .body(true) - .padding(.horizontal, Space.small) - .foregroundColor(isEnabled ? .accent : Color.accent.opacity(0.7)) - .frame(maxWidth: width == .full ? .infinity : width == .standart ? nil : size == .large ? Constants.heightL : Constants.heightM, - minHeight: size == .large ? ButtonStyleExtended.Constants.heightL : ButtonStyleExtended.Constants.heightM) - .opacity(configuration.isPressed ? 0.9 : 1) - .scaleEffect(configuration.isPressed ? 0.95 : 1) - } - } - - struct DeleteButton: View { - var size: ButtonSize - var rounded: ButtonRounded - var width: ButtonWidth - var shadow: Bool - let configuration: ButtonStyle.Configuration - @Environment(\.isEnabled) private var isEnabled: Bool - var body: some View { - configuration.label - .body(true) - .padding(.horizontal, Space.small) - .foregroundColor(isEnabled ? .error : Color.error.opacity(0.7)) - .frame(maxWidth: width == .full ? .infinity : width == .standart ? nil : size == .large ? Constants.heightL : Constants.heightM, - minHeight: size == .large ? ButtonStyleExtended.Constants.heightL : ButtonStyleExtended.Constants.heightM) - .opacity(configuration.isPressed ? 0.9 : 1) - .scaleEffect(configuration.isPressed ? 0.95 : 1) - } - } -} - -public extension Button { - /// Changes the appearance of the button - - @available(*, deprecated, message: "Use native buttonStyle", renamed: "buttonStyle") - func style(_ style: LegacyButtonType) -> some View { - buttonStyle(ButtonStyleExtended(style: style)) - } - - @available(*, deprecated, message: "Use native buttonStyle", renamed: "buttonStyle") - func style(_ style: LegacyButtonType, size: ButtonSize) -> some View { - buttonStyle(ButtonStyleExtended(style: style, size: size)) - } - - @available(*, deprecated, message: "Use native buttonStyle", renamed: "buttonStyle") - func style(_ style: LegacyButtonType, size: ButtonSize, shadow: Bool) -> some View { - buttonStyle(ButtonStyleExtended(style: style, size: size, shadow: shadow)) - } - - @available(*, deprecated, message: "Use native buttonStyle", renamed: "buttonStyle") - func style(_ style: LegacyButtonType, size: ButtonSize, rounded: ButtonRounded, width: ButtonWidth = .standart, shadow: Bool) -> some View { - buttonStyle(ButtonStyleExtended(style: style, size: size, rounded: rounded, width: width, shadow: shadow)) - } -} diff --git a/Sources/OversizeUI/Deprecated/HUDDeprecated.swift b/Sources/OversizeUI/Deprecated/HUDDeprecated.swift deleted file mode 100644 index 28defe4..0000000 --- a/Sources/OversizeUI/Deprecated/HUDDeprecated.swift +++ /dev/null @@ -1,236 +0,0 @@ -// -// Copyright © 2021 Alexander Romanov -// HUD.swift, created on 26.04.2021 -// - -import SwiftUI - -public enum HUDType { - case hud, alert -} - -public class HUDDeprecated: ObservableObject { - @Published public var isPresented: Bool = false - public var title: String - public var icon: IconsNames? - public var image: Image? - @Published public var type: HUDType = .hud - - public init(title: String = "") { - self.title = title - type = .hud - } - - public init(title: String = "", icon: IconsNames) { - self.title = title - self.icon = icon - type = .hud - } - - public init(title: String = "", image: Image) { - self.title = title - self.image = image - type = .alert - } - - public func show(title: String) { - self.title = title - type = .hud - withAnimation { - isPresented = true - } - } - - public func show(title: String, icon: IconsNames, type: HUDType = .hud) { - self.title = title - self.icon = icon - self.type = type - withAnimation { - isPresented = true - } - } - - public func show(title: String, image: Image, type: HUDType = .hud) { - self.title = title - self.image = image - self.type = type - withAnimation { - isPresented = true - } - } -} - -public extension View { - func hudDeprecated(isPresented: Binding, - type: Binding, - @ViewBuilder content: () -> some View) -> some View - { - ZStack(alignment: type.wrappedValue == .hud ? .top : .center) { - self - // .blur(radius: type.wrappedValue == .alert && isPresented.wrappedValue ? 10 : 0) - - if isPresented.wrappedValue { - HUDSurfaceView(type: type, content: content) - .transition( - type.wrappedValue == .hud - ? AnyTransition.move(edge: .top).combined(with: .opacity) - : AnyTransition.opacity.animation(.default) - ) - .onAppear { - DispatchQueue.main.asyncAfter(deadline: .now() + 3) { - withAnimation { - isPresented.wrappedValue = false - } - } - } - .zIndex(1) - } - } - } -} - -public struct HUDSurfaceView: View { - public let content: Content - @Binding public var type: HUDType - - public init(type: Binding, @ViewBuilder content: () -> Content) { - _type = type - self.content = content() - } - - public var body: some View { - #if os(iOS) - if #available(iOS 15.0, *) { - content - .padding(.top, topPadding) - .padding(.horizontal, horizontalPadding) - .padding(.bottom, bottomPadding) - .background(backgroundMaterial(type: type), - in: backgroundShape(type: type)) - .shadowElevaton(type == .hud ? .z2 : .z0) - } else { - content - .padding(.top, topPadding) - .padding(.horizontal, horizontalPadding) - .padding(.bottom, bottomPadding) - .background(background(type: type)) - } - #else - content - .padding(.top, topPadding) - .padding(.horizontal, horizontalPadding) - .padding(.bottom, bottomPadding) - .background(background(type: type)) - #endif - } - - #if os(iOS) - @available(iOS 15.0, *) - private func backgroundMaterial(type: HUDType) -> Material { - switch type { - case .hud: - .regular - case .alert: - .ultraThinMaterial - } - } - #endif - - private func backgroundShape(type: HUDType) -> AnyShape { - switch type { - case .hud: - AnyShape(Capsule()) - - case .alert: - AnyShape(RoundedRectangle(cornerRadius: Radius.medium, style: .continuous)) - } - } - - @ViewBuilder - private func background(type: HUDType) -> some View { - switch type { - case .hud: - Capsule() - .foregroundColor(Color.surfacePrimary) - .shadowElevaton(.z2) - case .alert: - RoundedRectangle(cornerRadius: 20, style: .continuous) - .foregroundColor(Color.surfacePrimary) - .shadowElevaton(.z4) - } - } - - var horizontalPadding: Space { - switch type { - case .hud: - .medium - case .alert: - .medium - } - } - - var topPadding: Space { - switch type { - case .hud: - .small - case .alert: - .xLarge - } - } - - var bottomPadding: Space { - switch type { - case .hud: - .small - case .alert: - .xLarge - } - } -} - -public struct HUDContent: View { - public var title: String - // public var icon: Icons? - public var image: Image? - public var type: HUDType - - public init(title: String, image: Image?, type: HUDType = .hud) { - self.title = title - self.image = image - self.type = type - } - - public var body: some View { - // Text(title) - // background(background(type: type)) - background - } - - private var background: some View { - Group { - switch type { - case .hud: - HStack { -// if let icon = hudState.icon { -// IconDeprecated(icon, color: .onSurfaceHighEmphasis) -// } - - Text(title) - .body() - .onSurfaceHighEmphasisForegroundColor() - } - case .alert: - VStack(spacing: .large) { - if let image { - image - } - - Text(title) - .title3() - .onSurfaceHighEmphasisForegroundColor() - } - .frame(minWidth: 225) - } - } - } -} diff --git a/Sources/OversizeUI/Deprecated/Icon.swift b/Sources/OversizeUI/Deprecated/Icon.swift index e51cfec..6eb0e89 100644 --- a/Sources/OversizeUI/Deprecated/Icon.swift +++ b/Sources/OversizeUI/Deprecated/Icon.swift @@ -30,12 +30,12 @@ public enum IconSizes: CaseIterable { } public struct IconDeprecated: View { - private enum Constants { + private enum Constants: Sendable { /// Size - static var small: CGFloat = Space.small.rawValue - static var medium: CGFloat = Space.medium.rawValue - static var large: CGFloat = Space.large.rawValue - static var xLarge: CGFloat = Space.xLarge.rawValue + static let small: CGFloat = Space.small.rawValue + static let medium: CGFloat = Space.medium.rawValue + static let large: CGFloat = Space.large.rawValue + static let xLarge: CGFloat = Space.xLarge.rawValue } let name: IconsNames? @@ -46,18 +46,18 @@ public struct IconDeprecated: View { public init(_ image: Image) { self.image = image size = .medium - color = Color.onBackgroundHighEmphasis + color = Color.onBackgroundPrimary name = nil } public init(_ name: IconsNames = .menu) { self.name = name size = .medium - color = Color.onBackgroundHighEmphasis + color = Color.onBackgroundPrimary image = nil } - public init(_ name: IconsNames = .menu, size: IconSizes = .medium, color: Color = .onBackgroundHighEmphasis) { + public init(_ name: IconsNames = .menu, size: IconSizes = .medium, color: Color = .onBackgroundPrimary) { self.name = name self.color = color self.size = size diff --git a/Sources/OversizeUI/Deprecated/PageView.swift b/Sources/OversizeUI/Deprecated/PageView.swift index 2004381..1b9a899 100644 --- a/Sources/OversizeUI/Deprecated/PageView.swift +++ b/Sources/OversizeUI/Deprecated/PageView.swift @@ -5,6 +5,7 @@ import SwiftUI +@available(iOS 15.0, macOS 14, tvOS 15.0, watchOS 9.0, *) public struct PageView: View where Content: View, LeadingBar: View, TrailingBar: View, TopToolbar: View, TitleLabel: View { @Environment(\.screenSize) var screenSize @@ -125,7 +126,7 @@ public struct PageView case .none: EmptyView() case .icon: - IconDeprecated(.xMini, color: .onSurfaceMediumEmphasis) + IconDeprecated(.xMini, color: .onSurfaceSecondary) .background { Circle() .fill(Color.backgroundTertiary) @@ -134,7 +135,7 @@ public struct PageView case let .label(text): Text(text) .subheadline(.bold) - .foregroundColor(.onSurfaceHighEmphasis) + .foregroundColor(.onSurfacePrimary) .padding(.horizontal, .xSmall) .background { RoundedRectangle(cornerRadius: .small, style: .continuous) @@ -271,6 +272,7 @@ public struct PageView } } +@available(iOS 15.0, macOS 14, tvOS 15.0, watchOS 9.0, *) public extension PageView { enum PageViewBottomType { case shadow, gradient, none @@ -281,6 +283,7 @@ public extension PageView { } } +@available(iOS 15.0, macOS 14, tvOS 15.0, watchOS 9.0, *) public extension PageView where LeadingBar == EmptyView, TitleLabel == EmptyView { init(_ title: String? = nil, onOffsetChanged: @escaping (CGFloat) -> Void = { _ in }, @@ -296,6 +299,7 @@ public extension PageView where LeadingBar == EmptyView, TitleLabel == EmptyView } } +@available(iOS 15.0, macOS 14, tvOS 15.0, watchOS 9.0, *) public extension PageView where TrailingBar == EmptyView, TitleLabel == EmptyView { init(_ title: String? = nil, onOffsetChanged: @escaping (CGFloat) -> Void = { _ in }, @@ -311,6 +315,7 @@ public extension PageView where TrailingBar == EmptyView, TitleLabel == EmptyVie } } +@available(iOS 15.0, macOS 14, tvOS 15.0, watchOS 9.0, *) public extension PageView where TrailingBar == EmptyView, LeadingBar == EmptyView, TitleLabel == EmptyView { init(_ title: String? = nil, onOffsetChanged: @escaping (CGFloat) -> Void = { _ in }, @@ -327,6 +332,7 @@ public extension PageView where TrailingBar == EmptyView, LeadingBar == EmptyVie } } +@available(iOS 15.0, macOS 14, tvOS 15.0, watchOS 9.0, *) public extension PageView where TrailingBar == EmptyView, LeadingBar == EmptyView, TopToolbar == EmptyView, TitleLabel == EmptyView { init(_ title: String? = nil, onOffsetChanged: @escaping (CGFloat) -> Void = { _ in }, @@ -344,6 +350,7 @@ public extension PageView where TrailingBar == EmptyView, LeadingBar == EmptyVie } } +@available(iOS 15.0, macOS 14, tvOS 15.0, watchOS 9.0, *) public extension PageView where LeadingBar == EmptyView, TopToolbar == EmptyView, TitleLabel == EmptyView { init(_ title: String? = nil, onOffsetChanged: @escaping (CGFloat) -> Void = { _ in }, @@ -360,6 +367,7 @@ public extension PageView where LeadingBar == EmptyView, TopToolbar == EmptyView } } +@available(iOS 15.0, macOS 14, tvOS 15.0, watchOS 9.0, *) public extension PageView where TrailingBar == EmptyView, TopToolbar == EmptyView, TitleLabel == EmptyView { init(_ title: String? = nil, onOffsetChanged: @escaping (CGFloat) -> Void = { _ in }, @@ -376,6 +384,7 @@ public extension PageView where TrailingBar == EmptyView, TopToolbar == EmptyVie } } +@available(iOS 15.0, macOS 14, tvOS 15.0, watchOS 9.0, *) public extension PageView where TopToolbar == EmptyView, TitleLabel == EmptyView { init(_ title: String? = nil, onOffsetChanged: @escaping (CGFloat) -> Void = { _ in }, @@ -391,6 +400,7 @@ public extension PageView where TopToolbar == EmptyView, TitleLabel == EmptyView } } +@available(iOS 15.0, macOS 14, tvOS 15.0, watchOS 9.0, *) public extension PageView where TrailingBar == EmptyView, LeadingBar == EmptyView, TopToolbar == EmptyView { init(_ title: String? = nil, onOffsetChanged: @escaping (CGFloat) -> Void = { _ in }, @@ -407,6 +417,7 @@ public extension PageView where TrailingBar == EmptyView, LeadingBar == EmptyVie } } +@available(iOS 15.0, macOS 14, tvOS 15.0, watchOS 9.0, *) public extension PageView where TrailingBar == EmptyView, TopToolbar == EmptyView { init(_ title: String? = nil, onOffsetChanged: @escaping (CGFloat) -> Void = { _ in }, @@ -422,6 +433,7 @@ public extension PageView where TrailingBar == EmptyView, TopToolbar == EmptyVie } } +@available(iOS 15.0, macOS 14, tvOS 15.0, watchOS 9.0, *) public extension PageView where LeadingBar == EmptyView, TopToolbar == EmptyView { init(_ title: String? = nil, onOffsetChanged: @escaping (CGFloat) -> Void = { _ in }, @@ -437,6 +449,7 @@ public extension PageView where LeadingBar == EmptyView, TopToolbar == EmptyView } } +@available(iOS 15.0, macOS 14, tvOS 15.0, watchOS 9.0, *) public extension PageView where TrailingBar == EmptyView { init(_ title: String? = nil, onOffsetChanged: @escaping (CGFloat) -> Void = { _ in }, @@ -451,6 +464,7 @@ public extension PageView where TrailingBar == EmptyView { } } +@available(iOS 15.0, macOS 14, tvOS 15.0, watchOS 9.0, *) public extension PageView where LeadingBar == EmptyView { init(_ title: String? = nil, onOffsetChanged: @escaping (CGFloat) -> Void = { _ in }, @@ -465,6 +479,7 @@ public extension PageView where LeadingBar == EmptyView { } } +@available(iOS 15.0, macOS 14, tvOS 15.0, watchOS 9.0, *) public extension PageView where TopToolbar == EmptyView { init(_ title: String? = nil, onOffsetChanged: @escaping (CGFloat) -> Void = { _ in }, @@ -479,6 +494,7 @@ public extension PageView where TopToolbar == EmptyView { } } +@available(iOS 15.0, macOS 14, tvOS 15.0, watchOS 9.0, *) public extension PageView where TitleLabel == EmptyView { init(_ title: String? = nil, onOffsetChanged: @escaping (CGFloat) -> Void = { _ in }, diff --git a/Sources/OversizeUI/Deprecated/RowDeprecated.swift b/Sources/OversizeUI/Deprecated/RowDeprecated.swift deleted file mode 100644 index a6bbcf2..0000000 --- a/Sources/OversizeUI/Deprecated/RowDeprecated.swift +++ /dev/null @@ -1,270 +0,0 @@ -// -// Copyright © 2021 Alexander Romanov -// RowDeprecated.swift, created on 11.09.2021 -// - -import SwiftUI - -public struct RowDeprecated: View { - @Environment(\.elevation) private var elevation: Elevation - @Environment(\.controlRadius) var controlRadius: Radius - @Environment(\.rowContentMargins) var controlPadding: EdgeSpaceInsets - @Environment(\.multilineTextAlignment) var multilineTextAlignment - @Environment(\.isPremium) var premiumStatus - @Environment(\.isAccent) var isAccent - @Environment(\.isLoading) var isLoading - - private let title: String - private let subtitle: String? - - private var leadingType: RowLeadingType? - private var trallingType: RowTrailingType? - - private let action: (() -> Void)? - - private var isPremiumOption: Bool = false - private var isShowArrowIcon: Bool = false - - private var iconBackgroundColor: Color? - - private var сlearButtonStyle: RowClearIconStyle = .default - private var сlearAction: (() -> Void)? - - private var isShowSubtitle: Bool { - (subtitle?.isEmpty) != nil - } - - public init(_ title: String, - subtitle: String? = nil, - action: (() -> Void)? = nil) - { - self.title = title - self.subtitle = subtitle - self.action = action - } - - @available(*, deprecated, message: "Use leading: {} and tralling: {}") - public init(_ title: String, - subtitle: String? = nil, - leadingType: RowLeadingType? = nil, - trallingType: RowTrailingType? = nil, - paddingHorizontal _: Space = .medium, - paddingVertical _: Space = .small, - action: (() -> Void)? = nil) - { - self.title = title - self.subtitle = subtitle - self.leadingType = leadingType - self.trallingType = trallingType - self.action = action - } - - public var body: some View { - if action != nil { - Button { - if isPremiumOption == false || (isPremiumOption && premiumStatus) { - action?() - } - } label: { - content(multilineTextAlignment) - } - .buttonStyle(.row) - } else { - content(multilineTextAlignment) - } - } - - @ViewBuilder - private func content(_ textAlignment: TextAlignment) -> some View { - VStack(alignment: .leading) { - HStack(spacing: .zero) { - if let leadingType { - RowLeading(leadingType, isShowSubtitle: isShowSubtitle, iconBackgroundColor: iconBackgroundColor) - .padding(.trailing, .small) - } - - if textAlignment == .trailing || textAlignment == .center { - Spacer() - } - - text - - premiumLabel - - if textAlignment == .leading || textAlignment == .center { - Spacer() - } - - if isLoading { - ProgressView() - .padding(.trailing, .xSmall) - } - - сlearButton - - if let trallingType { - RowTrailing(trallingType, isPremiumOption: isPremiumOption) - .padding(.leading, .xxSmall) - } - - if isShowArrowIcon { - IconDeprecated(.chevronRight, color: .onSurfaceDisabled) - } - } - } - .padding( - EdgeInsets(top: controlPadding.top, - leading: controlPadding.leading, - bottom: controlPadding.bottom, - trailing: controlPadding.trailing) - ) - } - - private var text: some View { - VStack(alignment: textAlignment, spacing: .xxxSmall) { - Text(title) - .headline(.semibold) - .foregroundColor(.onSurfaceHighEmphasis) - if let subtitle, !subtitle.isEmpty { - Text(subtitle) - .subheadline() - .foregroundColor(.onSurfaceMediumEmphasis) - } - } - .multilineTextAlignment(multilineTextAlignment) - } - - @ViewBuilder - private var сlearButton: some View { - if сlearAction != nil { - Button { - сlearAction?() - } label: { - ZStack { - IconDeprecated(.xMini, color: .onSurfaceDisabled) - .background( - RoundedRectangle(cornerRadius: .small, style: .continuous) - .fillSurfaceSecondary() - .opacity(сlearButtonStyle == .onSurface ? 1 : 0) - ) - } - } - } - } - - @ViewBuilder - private var premiumLabel: some View { - if isPremiumOption, premiumStatus == false { - PremiumLabel(text: "Pro", size: .small) - .padding(.leading, .small) - } - } - - private var textAlignment: HorizontalAlignment { - switch multilineTextAlignment { - case .leading: - .leading - case .center: - .center - case .trailing: - .trailing - } - } -} - -// MARK: - Modificators - -public extension RowDeprecated { - func premium(_ premium: Bool = true) -> RowDeprecated { - var control = self - control.isPremiumOption = premium - return control - } - - func rowArrow(_ showArrowIcon: Bool = true) -> RowDeprecated { - var control = self - control.isShowArrowIcon = showArrowIcon - return control - } - - func rowIconBackgroundColor(_ color: Color?) -> RowDeprecated { - var control = self - control.iconBackgroundColor = color - return control - } - - func rowClearButton(style: RowClearIconStyle = .default, action: (() -> Void)?) -> RowDeprecated { - var control = self - control.сlearButtonStyle = style - control.сlearAction = action - return control - } - - @available(*, deprecated, message: "Use leading: {} and tralling: {}") - func rowLeading(_ leading: RowLeadingType?) -> RowDeprecated { - var control = self - control.leadingType = leading - return control - } - - @available(*, deprecated, message: "Use leading: {} and tralling: {}") - func rowTrailing(_ trailing: RowTrailingType?) -> RowDeprecated { - var control = self - control.trallingType = trailing - return control - } -} - -// public extension View { -// func rowOnSurface(_ elevation: Elevation = .z4, backgroundColor: Color? = nil) -> some View { -// Surface { -// self.rowContentInset(.zero) -// } -// .surfaceBackgroundColor(backgroundColor) -// .elevation(elevation) -// } -// } - -// MARK: - Preview - -// swiftlint:disable all -struct ListRowDeprecated_Previews: PreviewProvider { - static var previews: some View { - VStack(spacing: .medium) { - Row("Title") - - Row("Title", subtitle: "Subtitle") - - Row("Title", subtitle: "Subtitle") { - IconDeprecated(.calendar) - } - - Radio(isOn: true, label: { - Row("Title", subtitle: "Subtitle") { - IconDeprecated(.calendar) - } - }) - - Checkbox(isOn: .constant(true), label: { - Row("Title", subtitle: "Subtitle") { - IconDeprecated(.calendar) - } - .rowOnSurface() - }) - -// Row("Title", subtitle: "Subtitle") -// .rowLeading(.avatar(AvatarView(firstName: "Name"))) -// .rowTrailing(.text("Text")) - -// Row("Title") -// .rowTrailing(.toggleWithArrowButton(isOn: .constant(true), action: nil)) -// -// Row("Title", subtitle: "Subtitle") -// .rowTrailing(.button("Button", action: {})) - - Row("Title", subtitle: "Red") - .premium() - } - .previewLayout(.fixed(width: 375, height: 70)) - } -} diff --git a/Sources/OversizeUI/Deprecated/RowLeading.swift b/Sources/OversizeUI/Deprecated/RowLeading.swift deleted file mode 100644 index c932d82..0000000 --- a/Sources/OversizeUI/Deprecated/RowLeading.swift +++ /dev/null @@ -1,76 +0,0 @@ -// -// Copyright © 2021 Alexander Romanov -// RowLeading.swift, created on 23.12.2022 -// - -import SwiftUI - -public enum RowLeadingType { - case icon(_ name: IconsNames) - case iconOnSurface(_ name: IconsNames) - case image(_ image: Image, color: Color? = .onSurfaceHighEmphasis) - case imageOnSurface(_ image: Image, color: Color? = nil) - case systemImage(_ imageName: String) - case avatar(_ avatar: Avatar) - - @available(*, deprecated, message: "Use leading: {}") - case view(_ view: AnyView) -} - -struct RowLeading: View { - private let type: RowLeadingType - private let isShowSubtitle: Bool - private let iconBackgroundColor: Color? - - init(_ type: RowLeadingType, isShowSubtitle: Bool = false, iconBackgroundColor: Color? = nil) { - self.type = type - self.isShowSubtitle = isShowSubtitle - self.iconBackgroundColor = iconBackgroundColor - } - - var body: some View { - switch type { - case let .icon(icon): - IconDeprecated(icon) - - case let .image(image, color): - image - .renderingMode(color != nil ? .template : .original) - .resizable() - .scaledToFill() - .foregroundColor(color) - .frame(width: isShowSubtitle ? 48 : 24, height: isShowSubtitle ? 48 : 24) - .cornerRadius(isShowSubtitle ? 4 : 2) - - case let .avatar(avatar): - avatar - - case let .iconOnSurface(icon): - Surface { - IconDeprecated(icon) - } - .surfaceStyle(.secondary) - .surfaceBackgroundColor(iconBackgroundColor) - .surfaceContentMargins(.xxSmall) - - case let .imageOnSurface(image, color): - Surface { - image - .renderingMode(.template) - .foregroundColor(color) - } - .surfaceStyle(.secondary) - .surfaceBackgroundColor(iconBackgroundColor) - .surfaceContentMargins(.xxSmall) - - case let .systemImage(systemImage): - Image(systemName: systemImage) - .foregroundColor(Color.onBackgroundHighEmphasis) - .font(.system(size: 24)) - .frame(width: 24, height: 24, alignment: .center) - - case let .view(view): - view - } - } -} diff --git a/Sources/OversizeUI/Deprecated/RowTrailing.swift b/Sources/OversizeUI/Deprecated/RowTrailing.swift deleted file mode 100644 index 8d1becf..0000000 --- a/Sources/OversizeUI/Deprecated/RowTrailing.swift +++ /dev/null @@ -1,119 +0,0 @@ -// -// Copyright © 2021 Alexander Romanov -// RowTrailing.swift, created on 23.12.2022 -// - -import SwiftUI - -import SwiftUI - -public enum RowTrailingType { - @available(*, deprecated, message: "Use Radio") - case radio(isOn: Binding) - @available(*, deprecated, message: "Use Checkbox") - case checkbox(isOn: Binding) - @available(*, deprecated, message: "Use Toogle") - case toggle(isOn: Binding) - @available(*, deprecated, message: "Use Toogle") - case toggleWithArrowButton(isOn: Binding, action: (() -> Void)? = nil) - @available(watchOS, unavailable) - case timePicker(date: Binding) - case arrowIcon - case text(_ text: String) - @available(tvOS, unavailable) - case button(_ text: String, action: () -> Void) -} - -struct RowTrailing: View { - @Environment(\.isPremium) var premiumStatus - - private let type: RowTrailingType - private let isPremiumOption: Bool - - init(_ type: RowTrailingType, isPremiumOption: Bool = false) { - self.type = type - self.isPremiumOption = isPremiumOption - } - - // swiftlint:disable function_body_length - var body: some View { - switch type { - case let .toggle(isOn): - Toggle(isOn: isOn) {} - .labelsHidden() - .disabled(isPremiumOption && premiumStatus == false) - - case let .radio(isOn: isOn): - ZStack { - Circle() - .stroke(Color.onSurfaceDisabled, lineWidth: 4) - .frame(width: 24, height: 24) - .cornerRadius(12) - .opacity(isOn.wrappedValue ? 0 : 1) - - Circle().fill(Color.accent) - .frame(width: 24, height: 24) - .cornerRadius(12) - .opacity(isOn.wrappedValue ? 1 : 0) - - Circle().fill(Color.white).frame(width: 8, height: 8) - .cornerRadius(4) - .opacity(isOn.wrappedValue ? 1 : 0) - } - - case let .checkbox(isOn: isOn): - ZStack { - RoundedRectangle(cornerRadius: Radius.small, style: .continuous) - .strokeBorder(Color.onSurfaceDisabled, lineWidth: 2.5) - .frame(width: 24, height: 24) - .opacity(isOn.wrappedValue ? 0 : 1) - - RoundedRectangle(cornerRadius: Radius.small, style: .continuous).fill(Color.accent) - .frame(width: 24, height: 24) - .opacity(isOn.wrappedValue ? 1 : 0) - - Image(systemName: "checkmark") - .font(.caption.weight(.black)) - .foregroundColor(.onPrimaryHighEmphasis) - .opacity(isOn.wrappedValue ? 1 : 0) - } - - case let .toggleWithArrowButton(isOn: isOn, action: action): - HStack { - Toggle(isOn: isOn) {} - .labelsHidden() - - Button(action: action ?? {}, label: { - IconDeprecated(.chevronRight, color: .onSurfaceDisabled) - }) - } - .disabled(isPremiumOption && premiumStatus == false) - - case .arrowIcon: - IconDeprecated(.chevronRight, color: .onSurfaceDisabled) - - case let .timePicker(date: date): - #if os(watchOS) - EmptyView() - #elseif os(iOS) - DatePicker("", selection: date, displayedComponents: .hourAndMinute) - .labelsHidden() - #endif - case let .text(text): - Text(text) - .subheadline() - .foregroundColor(.onSurfaceMediumEmphasis) - - case let .button(text, action: action): - #if os(tvOS) - EmptyView() - #else - Button(text, action: action) - .buttonStyle(.tertiary) - .controlBorderShape(.capsule) - .controlSize(.small) - .disabled(isPremiumOption && premiumStatus == false) - #endif - } - } -} diff --git a/Sources/OversizeUI/Deprecated/TextFieldDeprecated.swift b/Sources/OversizeUI/Deprecated/TextFieldDeprecated.swift deleted file mode 100644 index 464b5b6..0000000 --- a/Sources/OversizeUI/Deprecated/TextFieldDeprecated.swift +++ /dev/null @@ -1,173 +0,0 @@ -// -// -// Copyright © 2023 Aleksandr Romanov -// File.swift, created on 07.03.2023 -// - -import SwiftUI - -public struct DefaultPlaceholderTextFieldStyle: TextFieldStyle { - @Environment(\.theme) private var theme: ThemeSettings - - private let isFocused: Bool - - public init(focused: Bool = false) { - isFocused = focused - } - - public func _body(configuration: TextField<_Label>) -> some View { - VStack(alignment: .leading) { - configuration - .headline() - .foregroundColor(.onSurfaceHighEmphasis) - } - .padding() - .background( - RoundedRectangle( - cornerRadius: Radius.medium, - style: .continuous - ) - .fill(isFocused ? Color.surfacePrimary : Color.surfaceSecondary) - .overlay(overlay) - ) - } - - @ViewBuilder - var overlay: some View { - RoundedRectangle(cornerRadius: Radius.medium, - style: .continuous) - .stroke(overlayBorderColor, lineWidth: isFocused ? 2 : CGFloat(theme.borderSize)) - } - - var overlayBorderColor: Color { - if isFocused { - Color.accentColor - } else if theme.borderTextFields { - Color.border - } else { - Color.clear - } - } -} - -// swiftlint:disable identifier_name -public struct OverPlaceholderTextFieldStyle: TextFieldStyle { - @Environment(\.theme) private var theme: ThemeSettings - - public let placeholder: String - private let isFocused: Bool - - public init(placeholder: String, focused: Bool = false) { - self.placeholder = placeholder - isFocused = focused - } - - public func _body(configuration: TextField<_Label>) -> some View { - VStack(alignment: .leading) { - HStack { - Text(placeholder) - .subheadline(.semibold) - .foregroundColor(.onSurfaceHighEmphasis) - Spacer() - } - - VStack(alignment: .leading) { - configuration - .headline() - .foregroundColor(.onSurfaceHighEmphasis) - } - .padding() - .background( - RoundedRectangle(cornerRadius: Radius.medium, - style: .continuous) - .fill(isFocused ? Color.surfacePrimary : Color.surfaceSecondary) - .overlay(overlay) - ) - } - } - - @ViewBuilder - var overlay: some View { - RoundedRectangle(cornerRadius: Radius.medium, - style: .continuous) - .stroke(overlayBorderColor, lineWidth: isFocused ? 2 : CGFloat(theme.borderSize)) - } - - var overlayBorderColor: Color { - if isFocused { - Color.accentColor - } else if theme.borderTextFields { - Color.border - } else { - Color.clear - } - } -} - -// swiftlint:disable identifier_name -public struct InsidePlaceholderTextFieldStyle: TextFieldStyle { - @Environment(\.theme) private var theme: ThemeSettings - - public let placeholder: String - private let isFocused: Bool - - public init(placeholder: String, focused: Bool = false) { - self.placeholder = placeholder - isFocused = focused - } - - public func _body(configuration: TextField<_Label>) -> some View { - VStack(alignment: .leading, spacing: .zero) { - VStack(alignment: .leading) { - HStack { - Text(placeholder) - .subheadline(.semibold) - .foregroundColor(.onSurfaceMediumEmphasis) - Spacer() - } - - configuration - .headline() - .foregroundColor(.onSurfaceHighEmphasis) - } - .padding() - .background( - RoundedRectangle(cornerRadius: Radius.medium, - style: .continuous) - .fill(isFocused ? Color.surfacePrimary : Color.surfaceSecondary) - .overlay(overlay) - ) - } - } - - @ViewBuilder - var overlay: some View { - RoundedRectangle(cornerRadius: Radius.medium, - style: .continuous) - .stroke(overlayBorderColor, lineWidth: isFocused ? 2 : CGFloat(theme.borderSize)) - } - - var overlayBorderColor: Color { - if isFocused { - Color.accentColor - } else if theme.borderTextFields { - Color.border - } else { - Color.clear - } - } -} - -public extension TextFieldStyle where Self == DefaultPlaceholderTextFieldStyle { - @available(*, deprecated, message: "Use native @FocusState") - static func `default`(focused: Bool) -> DefaultPlaceholderTextFieldStyle { - DefaultPlaceholderTextFieldStyle(focused: focused) - } -} - -public extension TextFieldStyle where Self == InsidePlaceholderTextFieldStyle { - @available(*, deprecated, message: "Use .fieldLabelPosition(.overInput)") - static func placeholderInside(_ placeholder: String, focused: Bool = false) -> InsidePlaceholderTextFieldStyle { - InsidePlaceholderTextFieldStyle(placeholder: placeholder, focused: focused) - } -} diff --git a/Sources/OversizeUI/Extensions/View/View+NavigationView.swift b/Sources/OversizeUI/Deprecated/View+NavigationView.swift similarity index 100% rename from Sources/OversizeUI/Extensions/View/View+NavigationView.swift rename to Sources/OversizeUI/Deprecated/View+NavigationView.swift diff --git a/Sources/OversizeUI/Deprecated/View+ScrollViewOffset.swift b/Sources/OversizeUI/Deprecated/View+ScrollViewOffset.swift deleted file mode 100644 index 95dcaff..0000000 --- a/Sources/OversizeUI/Deprecated/View+ScrollViewOffset.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// Copyright © 2021 Alexander Romanov -// View+ScrollViewOffset.swift, created on 22.03.2022 -// - -import SwiftUI - -public extension View { - func scrollOffset(name: String = "", onChange: @escaping (CGFloat) -> Void) -> some View { - overlay( - GeometryReader { - Color.clear - .preference(key: ViewOffsetKey.self, - value: -$0.frame(in: name == "" ? .global : .named(name)).origin.y) - }) - .onPreferenceChange(ViewOffsetKey.self, perform: onChange) - } -} diff --git a/Sources/OversizeUI/Deprecated/ViewOffsetKey.swift b/Sources/OversizeUI/Deprecated/ViewOffsetKey.swift deleted file mode 100644 index 276f75a..0000000 --- a/Sources/OversizeUI/Deprecated/ViewOffsetKey.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// Copyright © 2021 Alexander Romanov -// ViewOffsetKey.swift, created on 22.03.2022 -// - -import SwiftUI - -public struct ViewOffsetKey: PreferenceKey { - public static var defaultValue = CGFloat.zero - public static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { - value += nextValue() - } -} diff --git a/Sources/OversizeUI/Extensions/Image/Image+Extensions.swift b/Sources/OversizeUI/Extensions/Image/Image+Extensions.swift index f89ef12..524c3b9 100644 --- a/Sources/OversizeUI/Extensions/Image/Image+Extensions.swift +++ b/Sources/OversizeUI/Extensions/Image/Image+Extensions.swift @@ -14,7 +14,7 @@ public extension Image { } public extension Image { - func icon(_ color: Color = Color.onSurfaceHighEmphasis) -> some View { + func icon(_ color: Color = Color.onSurfacePrimary) -> some View { renderingMode(.template) #if os(macOS) .resizable() @@ -28,7 +28,7 @@ public extension Image { } public extension Image { - func icon(_ color: Color = Color.onSurfaceHighEmphasis, size: IconSizes) -> some View { + func icon(_ color: Color = Color.onSurfacePrimary, size: IconSizes) -> some View { renderingMode(.template) .resizable() .frame( diff --git a/Sources/OversizeUI/Extensions/Padding/Padding.swift b/Sources/OversizeUI/Extensions/Padding/Padding.swift index 752152d..8efd8ad 100644 --- a/Sources/OversizeUI/Extensions/Padding/Padding.swift +++ b/Sources/OversizeUI/Extensions/Padding/Padding.swift @@ -8,7 +8,7 @@ import SwiftUI public struct PaddingModifier: ViewModifier { private let edges: Edge.Set private let length: Space - public init(edges: Edge.Set, length: Space) { + public nonisolated init(edges: Edge.Set, length: Space) { self.edges = edges self.length = length } @@ -20,7 +20,7 @@ public struct PaddingModifier: ViewModifier { public struct PaddingEdgeInsetsModifier: ViewModifier { private let insets: EdgeSpaceInsets - public init(insets: EdgeSpaceInsets) { + public nonisolated init(insets: EdgeSpaceInsets) { self.insets = insets } @@ -55,22 +55,22 @@ public struct ContentPaddingModifier: ViewModifier { public extension View { @_disfavoredOverload - func padding(_ edges: Edge.Set, _ length: Space) -> some View { + nonisolated func padding(_ edges: Edge.Set, _ length: Space) -> some View { modifier(PaddingModifier(edges: edges, length: length)) } @_disfavoredOverload - func padding(_ length: Space) -> some View { + nonisolated func padding(_ length: Space) -> some View { modifier(PaddingModifier(edges: Edge.Set.all, length: length)) } - func padding(_ insets: EdgeSpaceInsets) -> some View { + nonisolated func padding(_ insets: EdgeSpaceInsets) -> some View { modifier(PaddingEdgeInsetsModifier(insets: insets)) } } public extension View { - func paddingContent(_ edges: Edge.Set = .all) -> some View { + nonisolated func paddingContent(_ edges: Edge.Set = .all) -> some View { modifier(ContentPaddingModifier(edges: edges)) } } diff --git a/Sources/OversizeUI/Extensions/Spacing/Spacing.swift b/Sources/OversizeUI/Extensions/Spacing/Spacing.swift index ea583fd..5653638 100644 --- a/Sources/OversizeUI/Extensions/Spacing/Spacing.swift +++ b/Sources/OversizeUI/Extensions/Spacing/Spacing.swift @@ -7,49 +7,49 @@ import SwiftUI // swiftlint:disable line_length public extension HStack { - @inlinable init(alignment: VerticalAlignment = .center, spacing: Space, @ViewBuilder content: () -> Content) { + @inlinable nonisolated init(alignment: VerticalAlignment = .center, spacing: Space, @ViewBuilder content: () -> Content) { self = .init(alignment: alignment, spacing: spacing.rawValue, content: content) } } public extension VStack { - @inlinable init(alignment: HorizontalAlignment = .center, spacing: Space, @ViewBuilder content: () -> Content) { + @inlinable nonisolated init(alignment: HorizontalAlignment = .center, spacing: Space, @ViewBuilder content: () -> Content) { self = .init(alignment: alignment, spacing: spacing.rawValue, content: content) } } public extension LazyHStack { - @inlinable init(alignment: VerticalAlignment = .center, spacing: Space, @ViewBuilder content: () -> Content) { + @inlinable nonisolated init(alignment: VerticalAlignment = .center, spacing: Space, @ViewBuilder content: () -> Content) { self = .init(alignment: alignment, spacing: spacing.rawValue, content: content) } } public extension LazyVStack { - @inlinable init(alignment: HorizontalAlignment = .center, spacing: Space, @ViewBuilder content: () -> Content) { + @inlinable nonisolated init(alignment: HorizontalAlignment = .center, spacing: Space, @ViewBuilder content: () -> Content) { self = .init(alignment: alignment, spacing: spacing.rawValue, content: content) } } public extension LazyVGrid { - @inlinable init(columns: [GridItem], alignment: HorizontalAlignment = .center, spacing: Space, pinnedViews: PinnedScrollableViews = .init(), @ViewBuilder content: () -> Content) { + @inlinable nonisolated init(columns: [GridItem], alignment: HorizontalAlignment = .center, spacing: Space, pinnedViews: PinnedScrollableViews = .init(), @ViewBuilder content: () -> Content) { self = .init(columns: columns, alignment: alignment, spacing: spacing.rawValue, pinnedViews: pinnedViews, content: content) } } public extension Spacer { - @inlinable init(minLength: Space) { + @MainActor @inlinable @preconcurrency init(minLength: Space) { self = .init(minLength: minLength.rawValue) } } public extension GridItem { - @inlinable init(_ size: GridItem.Size = .flexible(), spacing: Space, alignment: Alignment? = nil) { + @inlinable nonisolated init(_ size: GridItem.Size = .flexible(), spacing: Space, alignment: Alignment? = nil) { self = .init(size, spacing: spacing.rawValue, alignment: alignment) } } public extension RoundedRectangle { - @inlinable init(cornerRadius: Radius, style: RoundedCornerStyle = .circular) { + @inlinable nonisolated init(cornerRadius: Radius, style: RoundedCornerStyle = .circular) { self = .init(cornerRadius: cornerRadius.rawValue, style: style) } } diff --git a/Sources/OversizeUI/Extensions/View/View+CornerRadius.swift b/Sources/OversizeUI/Extensions/View/View+CornerRadius.swift index c3a79c7..366b083 100644 --- a/Sources/OversizeUI/Extensions/View/View+CornerRadius.swift +++ b/Sources/OversizeUI/Extensions/View/View+CornerRadius.swift @@ -3,14 +3,22 @@ // View+CornerRadius.swift, created on 11.09.2021 // -#if os(iOS) import SwiftUI +#if canImport(UIKit) +public extension View { + func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View { + clipShape(RoundedRectangleCorner(radius: radius, corners: corners)) + } +} +#endif + +#if canImport(AppKit) public extension View { - @available(macOS, unavailable) @available(watchOS, unavailable) @available(tvOS, unavailable) - func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View { + @available(macOS 14, *) + func cornerRadius(_ radius: CGFloat, corners: RectCorner) -> some View { clipShape(RoundedRectangleCorner(radius: radius, corners: corners)) } } diff --git a/Sources/OversizeUI/Extensions/View/View+Cursor.swift b/Sources/OversizeUI/Extensions/View/View+Cursor.swift new file mode 100644 index 0000000..fe624f3 --- /dev/null +++ b/Sources/OversizeUI/Extensions/View/View+Cursor.swift @@ -0,0 +1,20 @@ +// +// Copyright © 2024 Alexander Romanov +// View+Cursor.swift, created on 23.10.2024 +// + +import SwiftUI + +#if os(macOS) +public extension View { + func cursor(_ cursor: NSCursor) -> some View { + onHover { inside in + if inside { + cursor.push() + } else { + NSCursor.pop() + } + } + } +} +#endif diff --git a/Sources/OversizeUI/Extensions/View/View+If.swift b/Sources/OversizeUI/Extensions/View/View+If.swift index ba9d399..624ccc6 100644 --- a/Sources/OversizeUI/Extensions/View/View+If.swift +++ b/Sources/OversizeUI/Extensions/View/View+If.swift @@ -7,7 +7,7 @@ import SwiftUI public extension View { @ViewBuilder - func `if`(_ condition: Bool, _ modifications: (Self) -> some View) -> some View { + nonisolated func `if`(_ condition: Bool, _ modifications: (Self) -> some View) -> some View { if condition { modifications(self) } else { diff --git a/Sources/OversizeUI/Extensions/View/View+Navigationable.swift b/Sources/OversizeUI/Extensions/View/View+Navigationable.swift deleted file mode 100644 index 3af643c..0000000 --- a/Sources/OversizeUI/Extensions/View/View+Navigationable.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// Copyright © 2021 Alexander Romanov -// View+Navigationable.swift, created on 14.04.2022 -// - -import SwiftUI - -public extension View { - @available(macOS, unavailable) - @available(watchOS, unavailable) - @available(tvOS, unavailable) - func navigationable(_ navigationBarHidden: Bool = true) -> some View { - NavigationView { - self - #if os(iOS) - .navigationBarHidden(navigationBarHidden) - #endif - } - } -} diff --git a/Sources/OversizeUI/Shapes/RoundedRectangleCorner.swift b/Sources/OversizeUI/Shapes/RoundedRectangleCorner.swift index 4de13c5..3ec5799 100644 --- a/Sources/OversizeUI/Shapes/RoundedRectangleCorner.swift +++ b/Sources/OversizeUI/Shapes/RoundedRectangleCorner.swift @@ -1,16 +1,22 @@ -// -// Copyright © 2021 Alexander Romanov -// RoundedRectangleCorner.swift, created on 11.09.2021 -// +import SwiftUI #if canImport(UIKit) -import SwiftUI +import UIKit +#elseif canImport(AppKit) +import AppKit +#endif +@available(iOS 15.0, macOS 14, tvOS 15.0, watchOS 9.0, *) public struct RoundedRectangleCorner: Shape { private var radius: CGFloat = .infinity + #if canImport(UIKit) private var corners: UIRectCorner = .allCorners + #else + private var corners: RectCorner = .allCorners + #endif + #if canImport(UIKit) public init(radius: CGFloat, corners: UIRectCorner) { self.radius = radius self.corners = corners @@ -20,11 +26,79 @@ public struct RoundedRectangleCorner: Shape { self.radius = radius.rawValue self.corners = corners } + #else + public init(radius: CGFloat, corners: RectCorner) { + self.radius = radius + self.corners = corners + } + + public init(radius: Radius, corners: RectCorner) { + self.radius = radius.rawValue + self.corners = corners + } + #endif public func path(in rect: CGRect) -> Path { + #if canImport(UIKit) let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius)) return Path(path.cgPath) + #else + let path = NSBezierPath() + + let topLeftRadius = corners.contains(.topLeft) ? radius : 0 + let topRightRadius = corners.contains(.topRight) ? radius : 0 + let bottomLeftRadius = corners.contains(.bottomLeft) ? radius : 0 + let bottomRightRadius = corners.contains(.bottomRight) ? radius : 0 + + path.move(to: CGPoint(x: rect.minX + topLeftRadius, y: rect.minY)) + + path.line(to: CGPoint(x: rect.maxX - topRightRadius, y: rect.minY)) + if topRightRadius > 0 { + path.curve(to: CGPoint(x: rect.maxX, y: rect.minY + topRightRadius), + controlPoint1: CGPoint(x: rect.maxX - topRightRadius / 2, y: rect.minY), + controlPoint2: CGPoint(x: rect.maxX, y: rect.minY + topRightRadius / 2)) + } + + path.line(to: CGPoint(x: rect.maxX, y: rect.maxY - bottomRightRadius)) + if bottomRightRadius > 0 { + path.curve(to: CGPoint(x: rect.maxX - bottomRightRadius, y: rect.maxY), + controlPoint1: CGPoint(x: rect.maxX, y: rect.maxY - bottomRightRadius / 2), + controlPoint2: CGPoint(x: rect.maxX - bottomRightRadius / 2, y: rect.maxY)) + } + + path.line(to: CGPoint(x: rect.minX + bottomLeftRadius, y: rect.maxY)) + if bottomLeftRadius > 0 { + path.curve(to: CGPoint(x: rect.minX, y: rect.maxY - bottomLeftRadius), + controlPoint1: CGPoint(x: rect.minX + bottomLeftRadius / 2, y: rect.maxY), + controlPoint2: CGPoint(x: rect.minX, y: rect.maxY - bottomLeftRadius / 2)) + } + + path.line(to: CGPoint(x: rect.minX, y: rect.minY + topLeftRadius)) + if topLeftRadius > 0 { + path.curve(to: CGPoint(x: rect.minX + topLeftRadius, y: rect.minY), + controlPoint1: CGPoint(x: rect.minX, y: rect.minY + topLeftRadius / 2), + controlPoint2: CGPoint(x: rect.minX + topLeftRadius / 2, y: rect.minY)) + } + + path.close() + return Path(path.cgPath) + #endif + } +} + +#if !canImport(UIKit) +public struct RectCorner: OptionSet, Sendable { + public let rawValue: Int + + public static let topLeft = RectCorner(rawValue: 1 << 0) + public static let topRight = RectCorner(rawValue: 1 << 1) + public static let bottomLeft = RectCorner(rawValue: 1 << 2) + public static let bottomRight = RectCorner(rawValue: 1 << 3) + public static let allCorners: RectCorner = [.topLeft, .topRight, .bottomLeft, .bottomRight] + + public init(rawValue: Int) { + self.rawValue = rawValue } } #endif