├── PracticeAppleSleep.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── jasperstritzke.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── PracticeAppleSleep ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── ContentView.swift ├── Extensions.swift ├── PracticeAppleSleepApp.swift └── Preview Content │ └── Preview Assets.xcassets │ └── Contents.json └── README.md /PracticeAppleSleep.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 56; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | C0039DDB290960F800F911B0 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0039DDA290960F800F911B0 /* Extensions.swift */; }; 11 | C0F8D81028FA26E8000FD752 /* PracticeAppleSleepApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0F8D80F28FA26E8000FD752 /* PracticeAppleSleepApp.swift */; }; 12 | C0F8D81228FA26E8000FD752 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0F8D81128FA26E8000FD752 /* ContentView.swift */; }; 13 | C0F8D81428FA26EA000FD752 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C0F8D81328FA26EA000FD752 /* Assets.xcassets */; }; 14 | C0F8D81728FA26EA000FD752 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C0F8D81628FA26EA000FD752 /* Preview Assets.xcassets */; }; 15 | C0F8D82128FA26EA000FD752 /* PracticeAppleSleepTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0F8D82028FA26EA000FD752 /* PracticeAppleSleepTests.swift */; }; 16 | C0F8D82B28FA26EA000FD752 /* PracticeAppleSleepUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0F8D82A28FA26EA000FD752 /* PracticeAppleSleepUITests.swift */; }; 17 | C0F8D82D28FA26EA000FD752 /* PracticeAppleSleepUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0F8D82C28FA26EA000FD752 /* PracticeAppleSleepUITestsLaunchTests.swift */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXContainerItemProxy section */ 21 | C0F8D81D28FA26EA000FD752 /* PBXContainerItemProxy */ = { 22 | isa = PBXContainerItemProxy; 23 | containerPortal = C0F8D80428FA26E7000FD752 /* Project object */; 24 | proxyType = 1; 25 | remoteGlobalIDString = C0F8D80B28FA26E7000FD752; 26 | remoteInfo = PracticeAppleSleep; 27 | }; 28 | C0F8D82728FA26EA000FD752 /* PBXContainerItemProxy */ = { 29 | isa = PBXContainerItemProxy; 30 | containerPortal = C0F8D80428FA26E7000FD752 /* Project object */; 31 | proxyType = 1; 32 | remoteGlobalIDString = C0F8D80B28FA26E7000FD752; 33 | remoteInfo = PracticeAppleSleep; 34 | }; 35 | /* End PBXContainerItemProxy section */ 36 | 37 | /* Begin PBXFileReference section */ 38 | C0039DDA290960F800F911B0 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; 39 | C0F8D80C28FA26E8000FD752 /* PracticeAppleSleep.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PracticeAppleSleep.app; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | C0F8D80F28FA26E8000FD752 /* PracticeAppleSleepApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PracticeAppleSleepApp.swift; sourceTree = ""; }; 41 | C0F8D81128FA26E8000FD752 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 42 | C0F8D81328FA26EA000FD752 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 43 | C0F8D81628FA26EA000FD752 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 44 | C0F8D81C28FA26EA000FD752 /* PracticeAppleSleepTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PracticeAppleSleepTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 45 | C0F8D82028FA26EA000FD752 /* PracticeAppleSleepTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PracticeAppleSleepTests.swift; sourceTree = ""; }; 46 | C0F8D82628FA26EA000FD752 /* PracticeAppleSleepUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PracticeAppleSleepUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | C0F8D82A28FA26EA000FD752 /* PracticeAppleSleepUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PracticeAppleSleepUITests.swift; sourceTree = ""; }; 48 | C0F8D82C28FA26EA000FD752 /* PracticeAppleSleepUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PracticeAppleSleepUITestsLaunchTests.swift; sourceTree = ""; }; 49 | /* End PBXFileReference section */ 50 | 51 | /* Begin PBXFrameworksBuildPhase section */ 52 | C0F8D80928FA26E7000FD752 /* Frameworks */ = { 53 | isa = PBXFrameworksBuildPhase; 54 | buildActionMask = 2147483647; 55 | files = ( 56 | ); 57 | runOnlyForDeploymentPostprocessing = 0; 58 | }; 59 | C0F8D81928FA26EA000FD752 /* Frameworks */ = { 60 | isa = PBXFrameworksBuildPhase; 61 | buildActionMask = 2147483647; 62 | files = ( 63 | ); 64 | runOnlyForDeploymentPostprocessing = 0; 65 | }; 66 | C0F8D82328FA26EA000FD752 /* Frameworks */ = { 67 | isa = PBXFrameworksBuildPhase; 68 | buildActionMask = 2147483647; 69 | files = ( 70 | ); 71 | runOnlyForDeploymentPostprocessing = 0; 72 | }; 73 | /* End PBXFrameworksBuildPhase section */ 74 | 75 | /* Begin PBXGroup section */ 76 | C0F8D80328FA26E7000FD752 = { 77 | isa = PBXGroup; 78 | children = ( 79 | C0F8D80E28FA26E8000FD752 /* PracticeAppleSleep */, 80 | C0F8D81F28FA26EA000FD752 /* PracticeAppleSleepTests */, 81 | C0F8D82928FA26EA000FD752 /* PracticeAppleSleepUITests */, 82 | C0F8D80D28FA26E8000FD752 /* Products */, 83 | ); 84 | sourceTree = ""; 85 | }; 86 | C0F8D80D28FA26E8000FD752 /* Products */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | C0F8D80C28FA26E8000FD752 /* PracticeAppleSleep.app */, 90 | C0F8D81C28FA26EA000FD752 /* PracticeAppleSleepTests.xctest */, 91 | C0F8D82628FA26EA000FD752 /* PracticeAppleSleepUITests.xctest */, 92 | ); 93 | name = Products; 94 | sourceTree = ""; 95 | }; 96 | C0F8D80E28FA26E8000FD752 /* PracticeAppleSleep */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | C0F8D80F28FA26E8000FD752 /* PracticeAppleSleepApp.swift */, 100 | C0F8D81128FA26E8000FD752 /* ContentView.swift */, 101 | C0F8D81328FA26EA000FD752 /* Assets.xcassets */, 102 | C0F8D81528FA26EA000FD752 /* Preview Content */, 103 | C0039DDA290960F800F911B0 /* Extensions.swift */, 104 | ); 105 | path = PracticeAppleSleep; 106 | sourceTree = ""; 107 | }; 108 | C0F8D81528FA26EA000FD752 /* Preview Content */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | C0F8D81628FA26EA000FD752 /* Preview Assets.xcassets */, 112 | ); 113 | path = "Preview Content"; 114 | sourceTree = ""; 115 | }; 116 | C0F8D81F28FA26EA000FD752 /* PracticeAppleSleepTests */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | C0F8D82028FA26EA000FD752 /* PracticeAppleSleepTests.swift */, 120 | ); 121 | path = PracticeAppleSleepTests; 122 | sourceTree = ""; 123 | }; 124 | C0F8D82928FA26EA000FD752 /* PracticeAppleSleepUITests */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | C0F8D82A28FA26EA000FD752 /* PracticeAppleSleepUITests.swift */, 128 | C0F8D82C28FA26EA000FD752 /* PracticeAppleSleepUITestsLaunchTests.swift */, 129 | ); 130 | path = PracticeAppleSleepUITests; 131 | sourceTree = ""; 132 | }; 133 | /* End PBXGroup section */ 134 | 135 | /* Begin PBXNativeTarget section */ 136 | C0F8D80B28FA26E7000FD752 /* PracticeAppleSleep */ = { 137 | isa = PBXNativeTarget; 138 | buildConfigurationList = C0F8D83028FA26EA000FD752 /* Build configuration list for PBXNativeTarget "PracticeAppleSleep" */; 139 | buildPhases = ( 140 | C0F8D80828FA26E7000FD752 /* Sources */, 141 | C0F8D80928FA26E7000FD752 /* Frameworks */, 142 | C0F8D80A28FA26E7000FD752 /* Resources */, 143 | ); 144 | buildRules = ( 145 | ); 146 | dependencies = ( 147 | ); 148 | name = PracticeAppleSleep; 149 | productName = PracticeAppleSleep; 150 | productReference = C0F8D80C28FA26E8000FD752 /* PracticeAppleSleep.app */; 151 | productType = "com.apple.product-type.application"; 152 | }; 153 | C0F8D81B28FA26EA000FD752 /* PracticeAppleSleepTests */ = { 154 | isa = PBXNativeTarget; 155 | buildConfigurationList = C0F8D83328FA26EA000FD752 /* Build configuration list for PBXNativeTarget "PracticeAppleSleepTests" */; 156 | buildPhases = ( 157 | C0F8D81828FA26EA000FD752 /* Sources */, 158 | C0F8D81928FA26EA000FD752 /* Frameworks */, 159 | C0F8D81A28FA26EA000FD752 /* Resources */, 160 | ); 161 | buildRules = ( 162 | ); 163 | dependencies = ( 164 | C0F8D81E28FA26EA000FD752 /* PBXTargetDependency */, 165 | ); 166 | name = PracticeAppleSleepTests; 167 | productName = PracticeAppleSleepTests; 168 | productReference = C0F8D81C28FA26EA000FD752 /* PracticeAppleSleepTests.xctest */; 169 | productType = "com.apple.product-type.bundle.unit-test"; 170 | }; 171 | C0F8D82528FA26EA000FD752 /* PracticeAppleSleepUITests */ = { 172 | isa = PBXNativeTarget; 173 | buildConfigurationList = C0F8D83628FA26EA000FD752 /* Build configuration list for PBXNativeTarget "PracticeAppleSleepUITests" */; 174 | buildPhases = ( 175 | C0F8D82228FA26EA000FD752 /* Sources */, 176 | C0F8D82328FA26EA000FD752 /* Frameworks */, 177 | C0F8D82428FA26EA000FD752 /* Resources */, 178 | ); 179 | buildRules = ( 180 | ); 181 | dependencies = ( 182 | C0F8D82828FA26EA000FD752 /* PBXTargetDependency */, 183 | ); 184 | name = PracticeAppleSleepUITests; 185 | productName = PracticeAppleSleepUITests; 186 | productReference = C0F8D82628FA26EA000FD752 /* PracticeAppleSleepUITests.xctest */; 187 | productType = "com.apple.product-type.bundle.ui-testing"; 188 | }; 189 | /* End PBXNativeTarget section */ 190 | 191 | /* Begin PBXProject section */ 192 | C0F8D80428FA26E7000FD752 /* Project object */ = { 193 | isa = PBXProject; 194 | attributes = { 195 | BuildIndependentTargetsInParallel = 1; 196 | LastSwiftUpdateCheck = 1400; 197 | LastUpgradeCheck = 1400; 198 | TargetAttributes = { 199 | C0F8D80B28FA26E7000FD752 = { 200 | CreatedOnToolsVersion = 14.0.1; 201 | }; 202 | C0F8D81B28FA26EA000FD752 = { 203 | CreatedOnToolsVersion = 14.0.1; 204 | TestTargetID = C0F8D80B28FA26E7000FD752; 205 | }; 206 | C0F8D82528FA26EA000FD752 = { 207 | CreatedOnToolsVersion = 14.0.1; 208 | TestTargetID = C0F8D80B28FA26E7000FD752; 209 | }; 210 | }; 211 | }; 212 | buildConfigurationList = C0F8D80728FA26E7000FD752 /* Build configuration list for PBXProject "PracticeAppleSleep" */; 213 | compatibilityVersion = "Xcode 14.0"; 214 | developmentRegion = en; 215 | hasScannedForEncodings = 0; 216 | knownRegions = ( 217 | en, 218 | Base, 219 | ); 220 | mainGroup = C0F8D80328FA26E7000FD752; 221 | productRefGroup = C0F8D80D28FA26E8000FD752 /* Products */; 222 | projectDirPath = ""; 223 | projectRoot = ""; 224 | targets = ( 225 | C0F8D80B28FA26E7000FD752 /* PracticeAppleSleep */, 226 | C0F8D81B28FA26EA000FD752 /* PracticeAppleSleepTests */, 227 | C0F8D82528FA26EA000FD752 /* PracticeAppleSleepUITests */, 228 | ); 229 | }; 230 | /* End PBXProject section */ 231 | 232 | /* Begin PBXResourcesBuildPhase section */ 233 | C0F8D80A28FA26E7000FD752 /* Resources */ = { 234 | isa = PBXResourcesBuildPhase; 235 | buildActionMask = 2147483647; 236 | files = ( 237 | C0F8D81728FA26EA000FD752 /* Preview Assets.xcassets in Resources */, 238 | C0F8D81428FA26EA000FD752 /* Assets.xcassets in Resources */, 239 | ); 240 | runOnlyForDeploymentPostprocessing = 0; 241 | }; 242 | C0F8D81A28FA26EA000FD752 /* Resources */ = { 243 | isa = PBXResourcesBuildPhase; 244 | buildActionMask = 2147483647; 245 | files = ( 246 | ); 247 | runOnlyForDeploymentPostprocessing = 0; 248 | }; 249 | C0F8D82428FA26EA000FD752 /* Resources */ = { 250 | isa = PBXResourcesBuildPhase; 251 | buildActionMask = 2147483647; 252 | files = ( 253 | ); 254 | runOnlyForDeploymentPostprocessing = 0; 255 | }; 256 | /* End PBXResourcesBuildPhase section */ 257 | 258 | /* Begin PBXSourcesBuildPhase section */ 259 | C0F8D80828FA26E7000FD752 /* Sources */ = { 260 | isa = PBXSourcesBuildPhase; 261 | buildActionMask = 2147483647; 262 | files = ( 263 | C0F8D81228FA26E8000FD752 /* ContentView.swift in Sources */, 264 | C0039DDB290960F800F911B0 /* Extensions.swift in Sources */, 265 | C0F8D81028FA26E8000FD752 /* PracticeAppleSleepApp.swift in Sources */, 266 | ); 267 | runOnlyForDeploymentPostprocessing = 0; 268 | }; 269 | C0F8D81828FA26EA000FD752 /* Sources */ = { 270 | isa = PBXSourcesBuildPhase; 271 | buildActionMask = 2147483647; 272 | files = ( 273 | C0F8D82128FA26EA000FD752 /* PracticeAppleSleepTests.swift in Sources */, 274 | ); 275 | runOnlyForDeploymentPostprocessing = 0; 276 | }; 277 | C0F8D82228FA26EA000FD752 /* Sources */ = { 278 | isa = PBXSourcesBuildPhase; 279 | buildActionMask = 2147483647; 280 | files = ( 281 | C0F8D82D28FA26EA000FD752 /* PracticeAppleSleepUITestsLaunchTests.swift in Sources */, 282 | C0F8D82B28FA26EA000FD752 /* PracticeAppleSleepUITests.swift in Sources */, 283 | ); 284 | runOnlyForDeploymentPostprocessing = 0; 285 | }; 286 | /* End PBXSourcesBuildPhase section */ 287 | 288 | /* Begin PBXTargetDependency section */ 289 | C0F8D81E28FA26EA000FD752 /* PBXTargetDependency */ = { 290 | isa = PBXTargetDependency; 291 | target = C0F8D80B28FA26E7000FD752 /* PracticeAppleSleep */; 292 | targetProxy = C0F8D81D28FA26EA000FD752 /* PBXContainerItemProxy */; 293 | }; 294 | C0F8D82828FA26EA000FD752 /* PBXTargetDependency */ = { 295 | isa = PBXTargetDependency; 296 | target = C0F8D80B28FA26E7000FD752 /* PracticeAppleSleep */; 297 | targetProxy = C0F8D82728FA26EA000FD752 /* PBXContainerItemProxy */; 298 | }; 299 | /* End PBXTargetDependency section */ 300 | 301 | /* Begin XCBuildConfiguration section */ 302 | C0F8D82E28FA26EA000FD752 /* Debug */ = { 303 | isa = XCBuildConfiguration; 304 | buildSettings = { 305 | ALWAYS_SEARCH_USER_PATHS = NO; 306 | CLANG_ANALYZER_NONNULL = YES; 307 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 308 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 309 | CLANG_ENABLE_MODULES = YES; 310 | CLANG_ENABLE_OBJC_ARC = YES; 311 | CLANG_ENABLE_OBJC_WEAK = YES; 312 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 313 | CLANG_WARN_BOOL_CONVERSION = YES; 314 | CLANG_WARN_COMMA = YES; 315 | CLANG_WARN_CONSTANT_CONVERSION = YES; 316 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 317 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 318 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 319 | CLANG_WARN_EMPTY_BODY = YES; 320 | CLANG_WARN_ENUM_CONVERSION = YES; 321 | CLANG_WARN_INFINITE_RECURSION = YES; 322 | CLANG_WARN_INT_CONVERSION = YES; 323 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 324 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 325 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 326 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 327 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 328 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 329 | CLANG_WARN_STRICT_PROTOTYPES = YES; 330 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 331 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 332 | CLANG_WARN_UNREACHABLE_CODE = YES; 333 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 334 | COPY_PHASE_STRIP = NO; 335 | DEBUG_INFORMATION_FORMAT = dwarf; 336 | ENABLE_STRICT_OBJC_MSGSEND = YES; 337 | ENABLE_TESTABILITY = YES; 338 | GCC_C_LANGUAGE_STANDARD = gnu11; 339 | GCC_DYNAMIC_NO_PIC = NO; 340 | GCC_NO_COMMON_BLOCKS = YES; 341 | GCC_OPTIMIZATION_LEVEL = 0; 342 | GCC_PREPROCESSOR_DEFINITIONS = ( 343 | "DEBUG=1", 344 | "$(inherited)", 345 | ); 346 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 347 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 348 | GCC_WARN_UNDECLARED_SELECTOR = YES; 349 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 350 | GCC_WARN_UNUSED_FUNCTION = YES; 351 | GCC_WARN_UNUSED_VARIABLE = YES; 352 | IPHONEOS_DEPLOYMENT_TARGET = 16.0; 353 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 354 | MTL_FAST_MATH = YES; 355 | ONLY_ACTIVE_ARCH = YES; 356 | SDKROOT = iphoneos; 357 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 358 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 359 | }; 360 | name = Debug; 361 | }; 362 | C0F8D82F28FA26EA000FD752 /* Release */ = { 363 | isa = XCBuildConfiguration; 364 | buildSettings = { 365 | ALWAYS_SEARCH_USER_PATHS = NO; 366 | CLANG_ANALYZER_NONNULL = YES; 367 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 368 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 369 | CLANG_ENABLE_MODULES = YES; 370 | CLANG_ENABLE_OBJC_ARC = YES; 371 | CLANG_ENABLE_OBJC_WEAK = YES; 372 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 373 | CLANG_WARN_BOOL_CONVERSION = YES; 374 | CLANG_WARN_COMMA = YES; 375 | CLANG_WARN_CONSTANT_CONVERSION = YES; 376 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 377 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 378 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 379 | CLANG_WARN_EMPTY_BODY = YES; 380 | CLANG_WARN_ENUM_CONVERSION = YES; 381 | CLANG_WARN_INFINITE_RECURSION = YES; 382 | CLANG_WARN_INT_CONVERSION = YES; 383 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 384 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 385 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 386 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 387 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 388 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 389 | CLANG_WARN_STRICT_PROTOTYPES = YES; 390 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 391 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 392 | CLANG_WARN_UNREACHABLE_CODE = YES; 393 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 394 | COPY_PHASE_STRIP = NO; 395 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 396 | ENABLE_NS_ASSERTIONS = NO; 397 | ENABLE_STRICT_OBJC_MSGSEND = YES; 398 | GCC_C_LANGUAGE_STANDARD = gnu11; 399 | GCC_NO_COMMON_BLOCKS = YES; 400 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 401 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 402 | GCC_WARN_UNDECLARED_SELECTOR = YES; 403 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 404 | GCC_WARN_UNUSED_FUNCTION = YES; 405 | GCC_WARN_UNUSED_VARIABLE = YES; 406 | IPHONEOS_DEPLOYMENT_TARGET = 16.0; 407 | MTL_ENABLE_DEBUG_INFO = NO; 408 | MTL_FAST_MATH = YES; 409 | SDKROOT = iphoneos; 410 | SWIFT_COMPILATION_MODE = wholemodule; 411 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 412 | VALIDATE_PRODUCT = YES; 413 | }; 414 | name = Release; 415 | }; 416 | C0F8D83128FA26EA000FD752 /* Debug */ = { 417 | isa = XCBuildConfiguration; 418 | buildSettings = { 419 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 420 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 421 | CODE_SIGN_STYLE = Automatic; 422 | CURRENT_PROJECT_VERSION = 1; 423 | DEVELOPMENT_ASSET_PATHS = "\"PracticeAppleSleep/Preview Content\""; 424 | DEVELOPMENT_TEAM = V76788P98D; 425 | ENABLE_PREVIEWS = YES; 426 | GENERATE_INFOPLIST_FILE = YES; 427 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 428 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 429 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 430 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 431 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 432 | LD_RUNPATH_SEARCH_PATHS = ( 433 | "$(inherited)", 434 | "@executable_path/Frameworks", 435 | ); 436 | MARKETING_VERSION = 1.0; 437 | PRODUCT_BUNDLE_IDENTIFIER = de.jasperstritzke.PracticeAppleSleep; 438 | PRODUCT_NAME = "$(TARGET_NAME)"; 439 | SWIFT_EMIT_LOC_STRINGS = YES; 440 | SWIFT_VERSION = 5.0; 441 | TARGETED_DEVICE_FAMILY = "1,2"; 442 | }; 443 | name = Debug; 444 | }; 445 | C0F8D83228FA26EA000FD752 /* Release */ = { 446 | isa = XCBuildConfiguration; 447 | buildSettings = { 448 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 449 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 450 | CODE_SIGN_STYLE = Automatic; 451 | CURRENT_PROJECT_VERSION = 1; 452 | DEVELOPMENT_ASSET_PATHS = "\"PracticeAppleSleep/Preview Content\""; 453 | DEVELOPMENT_TEAM = V76788P98D; 454 | ENABLE_PREVIEWS = YES; 455 | GENERATE_INFOPLIST_FILE = YES; 456 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 457 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 458 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 459 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 460 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 461 | LD_RUNPATH_SEARCH_PATHS = ( 462 | "$(inherited)", 463 | "@executable_path/Frameworks", 464 | ); 465 | MARKETING_VERSION = 1.0; 466 | PRODUCT_BUNDLE_IDENTIFIER = de.jasperstritzke.PracticeAppleSleep; 467 | PRODUCT_NAME = "$(TARGET_NAME)"; 468 | SWIFT_EMIT_LOC_STRINGS = YES; 469 | SWIFT_VERSION = 5.0; 470 | TARGETED_DEVICE_FAMILY = "1,2"; 471 | }; 472 | name = Release; 473 | }; 474 | C0F8D83428FA26EA000FD752 /* Debug */ = { 475 | isa = XCBuildConfiguration; 476 | buildSettings = { 477 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 478 | BUNDLE_LOADER = "$(TEST_HOST)"; 479 | CODE_SIGN_STYLE = Automatic; 480 | CURRENT_PROJECT_VERSION = 1; 481 | DEVELOPMENT_TEAM = V76788P98D; 482 | GENERATE_INFOPLIST_FILE = YES; 483 | IPHONEOS_DEPLOYMENT_TARGET = 16.0; 484 | MARKETING_VERSION = 1.0; 485 | PRODUCT_BUNDLE_IDENTIFIER = de.jasperstritzke.PracticeAppleSleepTests; 486 | PRODUCT_NAME = "$(TARGET_NAME)"; 487 | SWIFT_EMIT_LOC_STRINGS = NO; 488 | SWIFT_VERSION = 5.0; 489 | TARGETED_DEVICE_FAMILY = "1,2"; 490 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PracticeAppleSleep.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/PracticeAppleSleep"; 491 | }; 492 | name = Debug; 493 | }; 494 | C0F8D83528FA26EA000FD752 /* Release */ = { 495 | isa = XCBuildConfiguration; 496 | buildSettings = { 497 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 498 | BUNDLE_LOADER = "$(TEST_HOST)"; 499 | CODE_SIGN_STYLE = Automatic; 500 | CURRENT_PROJECT_VERSION = 1; 501 | DEVELOPMENT_TEAM = V76788P98D; 502 | GENERATE_INFOPLIST_FILE = YES; 503 | IPHONEOS_DEPLOYMENT_TARGET = 16.0; 504 | MARKETING_VERSION = 1.0; 505 | PRODUCT_BUNDLE_IDENTIFIER = de.jasperstritzke.PracticeAppleSleepTests; 506 | PRODUCT_NAME = "$(TARGET_NAME)"; 507 | SWIFT_EMIT_LOC_STRINGS = NO; 508 | SWIFT_VERSION = 5.0; 509 | TARGETED_DEVICE_FAMILY = "1,2"; 510 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PracticeAppleSleep.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/PracticeAppleSleep"; 511 | }; 512 | name = Release; 513 | }; 514 | C0F8D83728FA26EA000FD752 /* Debug */ = { 515 | isa = XCBuildConfiguration; 516 | buildSettings = { 517 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 518 | CODE_SIGN_STYLE = Automatic; 519 | CURRENT_PROJECT_VERSION = 1; 520 | DEVELOPMENT_TEAM = V76788P98D; 521 | GENERATE_INFOPLIST_FILE = YES; 522 | MARKETING_VERSION = 1.0; 523 | PRODUCT_BUNDLE_IDENTIFIER = de.jasperstritzke.PracticeAppleSleepUITests; 524 | PRODUCT_NAME = "$(TARGET_NAME)"; 525 | SWIFT_EMIT_LOC_STRINGS = NO; 526 | SWIFT_VERSION = 5.0; 527 | TARGETED_DEVICE_FAMILY = "1,2"; 528 | TEST_TARGET_NAME = PracticeAppleSleep; 529 | }; 530 | name = Debug; 531 | }; 532 | C0F8D83828FA26EA000FD752 /* Release */ = { 533 | isa = XCBuildConfiguration; 534 | buildSettings = { 535 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 536 | CODE_SIGN_STYLE = Automatic; 537 | CURRENT_PROJECT_VERSION = 1; 538 | DEVELOPMENT_TEAM = V76788P98D; 539 | GENERATE_INFOPLIST_FILE = YES; 540 | MARKETING_VERSION = 1.0; 541 | PRODUCT_BUNDLE_IDENTIFIER = de.jasperstritzke.PracticeAppleSleepUITests; 542 | PRODUCT_NAME = "$(TARGET_NAME)"; 543 | SWIFT_EMIT_LOC_STRINGS = NO; 544 | SWIFT_VERSION = 5.0; 545 | TARGETED_DEVICE_FAMILY = "1,2"; 546 | TEST_TARGET_NAME = PracticeAppleSleep; 547 | }; 548 | name = Release; 549 | }; 550 | /* End XCBuildConfiguration section */ 551 | 552 | /* Begin XCConfigurationList section */ 553 | C0F8D80728FA26E7000FD752 /* Build configuration list for PBXProject "PracticeAppleSleep" */ = { 554 | isa = XCConfigurationList; 555 | buildConfigurations = ( 556 | C0F8D82E28FA26EA000FD752 /* Debug */, 557 | C0F8D82F28FA26EA000FD752 /* Release */, 558 | ); 559 | defaultConfigurationIsVisible = 0; 560 | defaultConfigurationName = Release; 561 | }; 562 | C0F8D83028FA26EA000FD752 /* Build configuration list for PBXNativeTarget "PracticeAppleSleep" */ = { 563 | isa = XCConfigurationList; 564 | buildConfigurations = ( 565 | C0F8D83128FA26EA000FD752 /* Debug */, 566 | C0F8D83228FA26EA000FD752 /* Release */, 567 | ); 568 | defaultConfigurationIsVisible = 0; 569 | defaultConfigurationName = Release; 570 | }; 571 | C0F8D83328FA26EA000FD752 /* Build configuration list for PBXNativeTarget "PracticeAppleSleepTests" */ = { 572 | isa = XCConfigurationList; 573 | buildConfigurations = ( 574 | C0F8D83428FA26EA000FD752 /* Debug */, 575 | C0F8D83528FA26EA000FD752 /* Release */, 576 | ); 577 | defaultConfigurationIsVisible = 0; 578 | defaultConfigurationName = Release; 579 | }; 580 | C0F8D83628FA26EA000FD752 /* Build configuration list for PBXNativeTarget "PracticeAppleSleepUITests" */ = { 581 | isa = XCConfigurationList; 582 | buildConfigurations = ( 583 | C0F8D83728FA26EA000FD752 /* Debug */, 584 | C0F8D83828FA26EA000FD752 /* Release */, 585 | ); 586 | defaultConfigurationIsVisible = 0; 587 | defaultConfigurationName = Release; 588 | }; 589 | /* End XCConfigurationList section */ 590 | }; 591 | rootObject = C0F8D80428FA26E7000FD752 /* Project object */; 592 | } 593 | -------------------------------------------------------------------------------- /PracticeAppleSleep.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PracticeAppleSleep.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /PracticeAppleSleep.xcodeproj/xcuserdata/jasperstritzke.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /PracticeAppleSleep.xcodeproj/xcuserdata/jasperstritzke.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | PracticeAppleSleep.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /PracticeAppleSleep/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /PracticeAppleSleep/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "platform" : "ios", 6 | "size" : "1024x1024" 7 | } 8 | ], 9 | "info" : { 10 | "author" : "xcode", 11 | "version" : 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /PracticeAppleSleep/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /PracticeAppleSleep/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // PracticeAppleSleep 4 | // 5 | // Created by Jasper Stritzke on 14.10.22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct Config { 11 | let radius: CGFloat 12 | let knobRadius: CGFloat 13 | //the smallest amount of hours sleep, you can configure 14 | let minSleepDuration: CGFloat 15 | //24 hours in minutes, seperated in 15 minute blocks 16 | let countSteps: Int 17 | 18 | var diameter: CGFloat { 19 | return radius * 2 20 | } 21 | 22 | var knobDiameter: CGFloat { 23 | return knobRadius * 2 24 | } 25 | } 26 | 27 | let config = Config( 28 | radius: 150.0 , 29 | knobRadius: 22.0, 30 | minSleepDuration: 1.0, 31 | countSteps: 24*60/10 32 | ) 33 | 34 | struct ContentView: View { 35 | 36 | @State var rotationSleep = 0.0 37 | @State var rotationWakeup = 90.0 38 | 39 | var totalSleepDuration: Double { 40 | let mappedSleep = mapRotationToTime(degrees: rotationSleep) 41 | var mappedWakeup = mapRotationToTime(degrees: rotationWakeup) 42 | 43 | if mappedSleep > mappedWakeup { 44 | mappedWakeup += 24 45 | } 46 | 47 | return mappedWakeup - mappedSleep; 48 | } 49 | 50 | func getFormattedSleepDuration() -> String { 51 | let duration = totalSleepDuration 52 | 53 | //truncuate decimals 54 | let hours = Int(duration) 55 | 56 | //remove full numbers and use rest to calculate the fraction of an hour 57 | var min = Int((duration-Double(hours))*60) 58 | 59 | //Round angle to always snap to minutes, that are multiples of 5 (5, 10, 15, 20, 25, ...) 60 | while min % 5 != 0 { 61 | min -= 1 62 | } 63 | 64 | if min == 0 { 65 | return "\(hours) Std." 66 | } 67 | 68 | return "\(hours) Std. \(min) Min." 69 | } 70 | 71 | func mapRotationToTime(degrees: Double) -> Double { 72 | let steps = 24.0 / 360.0 73 | 74 | return steps * degrees 75 | } 76 | 77 | var connectorLength: Double { 78 | if rotationSleep > rotationWakeup { 79 | return abs((rotationSleep-360-rotationWakeup)/360.0) 80 | } 81 | 82 | return (rotationWakeup-rotationSleep)/360.0 83 | } 84 | 85 | func getSleepTime() -> String { 86 | let sleepTimeHour = 24 / 360 * rotationSleep; 87 | 88 | //truncuate decimals 89 | let onlyHours = Int(sleepTimeHour) 90 | 91 | //remove full numbers and use rest to calculate the fraction of an hour 92 | var onlyMinutes = Int((sleepTimeHour-Double(onlyHours))*60) 93 | 94 | //Round angle to always snap to minutes, that are multiples of 5 (5, 10, 15, 20, 25, ...) 95 | while onlyMinutes % 5 != 0 { 96 | onlyMinutes -= 1 97 | } 98 | 99 | return (onlyHours <= 9 ? "0" + String(onlyHours) : String(onlyHours)) + ":" + (onlyMinutes <= 9 ? "0" + String(onlyMinutes) : String(onlyMinutes)) 100 | } 101 | 102 | func getWakeUpTime() -> String { 103 | let sleepTimeHour = 24 / 360 * rotationWakeup; 104 | 105 | //truncuate decimals 106 | let onlyHours = Int(sleepTimeHour) 107 | 108 | //remove full numbers and use rest to calculate the fraction of an hour 109 | var onlyMinutes = Int((sleepTimeHour-Double(onlyHours))*60) 110 | 111 | //Round angle to always snap to minutes, that are multiples of 5 (5, 10, 15, 20, 25, ...) 112 | while onlyMinutes % 5 != 0 { 113 | onlyMinutes -= 1 114 | } 115 | 116 | return (onlyHours <= 9 ? "0" + String(onlyHours) : String(onlyHours)) + ":" + (onlyMinutes <= 9 ? "0" + String(onlyMinutes) : String(onlyMinutes)) 117 | } 118 | 119 | let grayColor = Color(red: 44/255, green: 44/255, blue: 46/255) 120 | let labelColor = Color(red: 115/255, green: 114/255, blue: 118/255) 121 | let knobHover = Color(red: 28/255, green: 28/255, blue: 29/255) 122 | 123 | @State var didTapSleep = false 124 | @State var didTapWakeup = false 125 | 126 | var body: some View { 127 | ZStack { 128 | grayColor.edgesIgnoringSafeArea(.all) 129 | 130 | VStack { 131 | HStack(spacing: 50) { 132 | VStack { 133 | HStack { 134 | Image(systemName: "bed.double") 135 | .foregroundColor(.mint) 136 | .font(.system(size: 12)) 137 | 138 | Text("Schlafenszeit".uppercased()) 139 | .bold() 140 | .foregroundColor(.white.opacity(0.3)) 141 | .font(.system(size: 14)) 142 | } 143 | Text(getSleepTime()) 144 | .bold() 145 | .foregroundColor(.white) 146 | .font(.system(size: 22)) 147 | 148 | Text("Morgen") 149 | .bold() 150 | .font(.system(size: 14)) 151 | .foregroundColor(.white.opacity(0.5)) 152 | } 153 | 154 | VStack { 155 | HStack { 156 | Image(systemName: "alarm") 157 | .foregroundColor(.red) 158 | .font(.system(size: 12)) 159 | 160 | Text("Wecker".uppercased()) 161 | .bold() 162 | .foregroundColor(.white.opacity(0.3)) 163 | .font(.system(size: 14)) 164 | } 165 | Text(getWakeUpTime()) 166 | .bold() 167 | .foregroundColor(.white) 168 | .font(.system(size: 22)) 169 | 170 | Text("Morgen") 171 | .bold() 172 | .font(.system(size: 14)) 173 | .foregroundColor(.white.opacity(0.5)) 174 | } 175 | } 176 | .padding(.horizontal) 177 | .padding(.top) 178 | 179 | Spacer() 180 | 181 | HStack { 182 | Text(String(getFormattedSleepDuration())) 183 | .bold() 184 | .foregroundColor(.white) 185 | .font(.system(size: 22)) 186 | } 187 | } 188 | 189 | ZStack { 190 | Circle() 191 | .stroke(.black.opacity(0.9), lineWidth: config.knobDiameter*1.32) 192 | .frame(width: config.diameter, height: config.diameter) 193 | 194 | Circle() 195 | .trim(from: 0, to: connectorLength) 196 | .stroke(grayColor, lineWidth: config.knobDiameter) 197 | .frame(width: config.diameter, height: config.diameter) 198 | .rotationEffect(Angle(degrees: rotationSleep-90)) 199 | // .gesture( 200 | // DragGesture(minimumDistance: 0) 201 | // ) 202 | 203 | ForEach((1...config.countSteps).reversed(), id: \.self) { i in 204 | RoundedRectangle(cornerRadius: 5) 205 | .frame(width: 3, height: config.knobRadius*0.9) 206 | .foregroundColor(.black.opacity(0.3)) 207 | .offset(y: config.radius) 208 | .rotationEffect( 209 | Angle(degrees: 360/Double(config.countSteps) * Double(i)) 210 | ) 211 | } 212 | 213 | ForEach(1...(24*4), id: \.self) { i in 214 | RoundedRectangle(cornerRadius: 5) 215 | .frame(width: 2, height: i % 4 != 0 ? 4 : 8) 216 | .foregroundColor(.white.opacity(0.2)) 217 | .offset(y: config.radius - (i % 4 != 0 ? 40 : 42)) 218 | .rotationEffect( 219 | Angle(degrees: 360/(24*4) * Double(i)) 220 | ) 221 | } 222 | 223 | ForEach(1...24, id: \.self) { hour in 224 | // if hour % 2 == 0 { 225 | ZStack { 226 | Text(String(getHourStringByInt(hour))) 227 | .font(.system(size: 16)) 228 | .bold() 229 | .foregroundColor(hour % 3 == 0 ? .white : .white.opacity(0.5)) 230 | .rotationEffect(Angle(degrees: -(360.0/24*Double(hour)))) 231 | } 232 | .offset(y: -(config.radius - 65)) 233 | .rotationEffect(Angle(degrees: 360.0/24*Double(hour))) 234 | // } 235 | } 236 | 237 | Image(systemName: "sun.max.fill") 238 | .foregroundColor(.yellow) 239 | .font(.system(size: 18)) 240 | .offset(y: 55) 241 | 242 | Image(systemName: "sparkles") 243 | .foregroundColor(Color.mint) 244 | .font(.system(size: 18)) 245 | .offset(y: -55) 246 | 247 | 248 | // Knob - start sleeping 249 | getKnob(icon: "bed.double", iconRotation: -rotationSleep, isTapped: didTapSleep, rotate: false) 250 | .rotationEffect(Angle(degrees: rotationSleep)) 251 | .gesture( 252 | DragGesture(minimumDistance: 0) 253 | .onChanged { action in 254 | change(action, ChangeType.SLEEP) 255 | } 256 | ) 257 | 258 | // Knob - wake up 259 | getKnob(icon: "alarm", iconRotation: -rotationWakeup, isTapped: didTapWakeup, rotate: true) 260 | .rotationEffect(Angle(degrees: rotationWakeup)) 261 | .gesture( 262 | DragGesture(minimumDistance: 0) 263 | .onChanged { action in 264 | change(action, ChangeType.WAKEUP) 265 | } 266 | ) 267 | } 268 | } 269 | } 270 | 271 | func getHourStringByInt(_ i: Int) -> String { 272 | if i == 24 { 273 | return "0" 274 | } 275 | 276 | if i % 2 == 0 { 277 | return String(i) 278 | } 279 | 280 | return "" 281 | } 282 | 283 | func getKnob(icon: String, iconRotation: Double, isTapped: Bool, rotate: Bool) -> some View { 284 | return ZStack { 285 | Circle() 286 | .trim(from: 0.25, to: 0.75) 287 | .foregroundColor(grayColor) 288 | .rotationEffect(Angle(degrees: rotate ? 180 : 0)) 289 | 290 | Circle() 291 | .foregroundColor(isTapped ? knobHover : grayColor) 292 | .padding(5) 293 | Image(systemName: icon) 294 | .foregroundColor(labelColor) 295 | .font(.system(size: config.knobRadius/1.6)) 296 | .bold() 297 | .rotationEffect(Angle(degrees: iconRotation)) 298 | } 299 | .frame(width: config.knobDiameter, height: config.knobDiameter) 300 | .offset(y: -config.radius) 301 | } 302 | 303 | enum ChangeType { 304 | case SLEEP 305 | case WAKEUP 306 | case CONNECTOR 307 | } 308 | 309 | func calcAngle(x: CGFloat, y: CGFloat) -> Double { 310 | let vector = CGVector(dx: x, dy: y) 311 | 312 | var angle = (atan2( 313 | vector.dy - config.knobRadius, vector.dx - config.knobRadius 314 | ) + .pi / 2) / .pi * 180 315 | 316 | if angle < 0 { 317 | angle += 360 318 | } 319 | 320 | return angle 321 | } 322 | 323 | func change(_ action: DragGesture.Value, _ type: ChangeType) { 324 | let angle = calcAngle(x: action.location.x, y: action.location.y) 325 | 326 | switch type { 327 | case ChangeType.SLEEP: 328 | rotationSleep = angle 329 | break 330 | case ChangeType.WAKEUP: 331 | rotationWakeup = angle 332 | break 333 | default: 334 | return 335 | } 336 | } 337 | } 338 | 339 | struct ContentView_Previews: PreviewProvider { 340 | static var previews: some View { 341 | ContentView() 342 | } 343 | } 344 | -------------------------------------------------------------------------------- /PracticeAppleSleep/Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extensions.swift 3 | // PracticeAppleSleep 4 | // 5 | // Created by Jasper Stritzke on 26.10.22. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct ButtonPressHandler: ButtonStyle { 12 | var action: () -> () 13 | func makeBody(configuration: Self.Configuration) -> some View { 14 | configuration.label 15 | .foregroundColor(configuration.isPressed ? 16 | Color.blue.opacity(0.7) : Color.blue) // just to look like system 17 | .onChange(of: configuration.isPressed) { 18 | if $0 { 19 | action() 20 | } 21 | } 22 | } 23 | } 24 | 25 | extension Button { 26 | func onTapEnded(_ action: @escaping () -> Void) -> some View { 27 | buttonStyle(ButtonPressHandler(action: action)) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /PracticeAppleSleep/PracticeAppleSleepApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PracticeAppleSleepApp.swift 3 | // PracticeAppleSleep 4 | // 5 | // Created by Jasper Stritzke on 14.10.22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct PracticeAppleSleepApp: App { 12 | var body: some Scene { 13 | WindowGroup { 14 | ContentView() 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /PracticeAppleSleep/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ios-sleeptime-circular-slider 2 | ###### _i had no idea how to name this project ^^_ 3 | 4 | I really like the iOS interaction to set your sleep time - so I tried cloning it. 5 | 6 | I definetly could optimize the maths behind it and also fix one or two minor visual glitches. 7 | 8 | Additionaly I'd like to add the interaction to rotate the entire wheel in near future. 9 | 10 | --- 11 | 12 | ### Screenshot 13 | 14 | Screenshot 15 | --------------------------------------------------------------------------------