├── ContinuityTransition.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── victor.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── ContinuityTransition ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── gray0.colorset │ │ └── Contents.json │ ├── gray100.colorset │ │ └── Contents.json │ ├── gray200.colorset │ │ └── Contents.json │ ├── gray300.colorset │ │ └── Contents.json │ ├── gray400.colorset │ │ └── Contents.json │ ├── gray50.colorset │ │ └── Contents.json │ ├── gray500.colorset │ │ └── Contents.json │ ├── gray600.colorset │ │ └── Contents.json │ ├── gray700.colorset │ │ └── Contents.json │ ├── gray800.colorset │ │ └── Contents.json │ └── gray900.colorset │ │ └── Contents.json ├── ContentView.swift ├── ContinuityTransitionApp.swift ├── Managers │ ├── BouncyButton.swift │ ├── Colors.swift │ └── HapticsManager.swift ├── Model │ └── Model.swift ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── ViewModel │ └── HomeViewModel.swift └── Views │ ├── FirstExpandedView.swift │ ├── HomeView.swift │ ├── SecondExpandedView.swift │ └── ThirdExpandedView.swift └── README.md /ContinuityTransition.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 56; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 570B4ECA2BB0B99700D9B628 /* SecondExpandedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 570B4EC92BB0B99700D9B628 /* SecondExpandedView.swift */; }; 11 | 570B4ECC2BB0C34C00D9B628 /* ThirdExpandedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 570B4ECB2BB0C34C00D9B628 /* ThirdExpandedView.swift */; }; 12 | 572604FA2BAF5A3800F5F47A /* ContinuityTransitionApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 572604F92BAF5A3800F5F47A /* ContinuityTransitionApp.swift */; }; 13 | 572604FC2BAF5A3800F5F47A /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 572604FB2BAF5A3800F5F47A /* ContentView.swift */; }; 14 | 572604FE2BAF5A3A00F5F47A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 572604FD2BAF5A3A00F5F47A /* Assets.xcassets */; }; 15 | 572605012BAF5A3A00F5F47A /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 572605002BAF5A3A00F5F47A /* Preview Assets.xcassets */; }; 16 | 5726050B2BAF5CE600F5F47A /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5726050A2BAF5CE600F5F47A /* HomeViewModel.swift */; }; 17 | 5726050D2BAF5CFE00F5F47A /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5726050C2BAF5CFE00F5F47A /* HomeView.swift */; }; 18 | 5726050F2BAF682F00F5F47A /* HapticsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5726050E2BAF682F00F5F47A /* HapticsManager.swift */; }; 19 | 572605112BAF6E5D00F5F47A /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 572605102BAF6E5D00F5F47A /* Colors.swift */; }; 20 | 572605132BAF6EAC00F5F47A /* BouncyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 572605122BAF6EAC00F5F47A /* BouncyButton.swift */; }; 21 | 572605162BAF719B00F5F47A /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 572605152BAF719B00F5F47A /* Model.swift */; }; 22 | 572605182BAF84D800F5F47A /* FirstExpandedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 572605172BAF84D800F5F47A /* FirstExpandedView.swift */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | 570B4EC92BB0B99700D9B628 /* SecondExpandedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondExpandedView.swift; sourceTree = ""; }; 27 | 570B4ECB2BB0C34C00D9B628 /* ThirdExpandedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThirdExpandedView.swift; sourceTree = ""; }; 28 | 572604F62BAF5A3800F5F47A /* ContinuityTransition.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ContinuityTransition.app; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | 572604F92BAF5A3800F5F47A /* ContinuityTransitionApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContinuityTransitionApp.swift; sourceTree = ""; }; 30 | 572604FB2BAF5A3800F5F47A /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 31 | 572604FD2BAF5A3A00F5F47A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 32 | 572605002BAF5A3A00F5F47A /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 33 | 5726050A2BAF5CE600F5F47A /* HomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewModel.swift; sourceTree = ""; }; 34 | 5726050C2BAF5CFE00F5F47A /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = ""; }; 35 | 5726050E2BAF682F00F5F47A /* HapticsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HapticsManager.swift; sourceTree = ""; }; 36 | 572605102BAF6E5D00F5F47A /* Colors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = ""; }; 37 | 572605122BAF6EAC00F5F47A /* BouncyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BouncyButton.swift; sourceTree = ""; }; 38 | 572605152BAF719B00F5F47A /* Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Model.swift; sourceTree = ""; }; 39 | 572605172BAF84D800F5F47A /* FirstExpandedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstExpandedView.swift; sourceTree = ""; }; 40 | /* End PBXFileReference section */ 41 | 42 | /* Begin PBXFrameworksBuildPhase section */ 43 | 572604F32BAF5A3800F5F47A /* Frameworks */ = { 44 | isa = PBXFrameworksBuildPhase; 45 | buildActionMask = 2147483647; 46 | files = ( 47 | ); 48 | runOnlyForDeploymentPostprocessing = 0; 49 | }; 50 | /* End PBXFrameworksBuildPhase section */ 51 | 52 | /* Begin PBXGroup section */ 53 | 572604ED2BAF5A3800F5F47A = { 54 | isa = PBXGroup; 55 | children = ( 56 | 572604F82BAF5A3800F5F47A /* ContinuityTransition */, 57 | 572604F72BAF5A3800F5F47A /* Products */, 58 | ); 59 | sourceTree = ""; 60 | }; 61 | 572604F72BAF5A3800F5F47A /* Products */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | 572604F62BAF5A3800F5F47A /* ContinuityTransition.app */, 65 | ); 66 | name = Products; 67 | sourceTree = ""; 68 | }; 69 | 572604F82BAF5A3800F5F47A /* ContinuityTransition */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | 572604F92BAF5A3800F5F47A /* ContinuityTransitionApp.swift */, 73 | 572605142BAF719100F5F47A /* Model */, 74 | 572604FB2BAF5A3800F5F47A /* ContentView.swift */, 75 | 572605092BAF5CB700F5F47A /* Managers */, 76 | 572605082BAF5CAD00F5F47A /* ViewModel */, 77 | 572605072BAF5CA500F5F47A /* Views */, 78 | 572604FD2BAF5A3A00F5F47A /* Assets.xcassets */, 79 | 572604FF2BAF5A3A00F5F47A /* Preview Content */, 80 | ); 81 | path = ContinuityTransition; 82 | sourceTree = ""; 83 | }; 84 | 572604FF2BAF5A3A00F5F47A /* Preview Content */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | 572605002BAF5A3A00F5F47A /* Preview Assets.xcassets */, 88 | ); 89 | path = "Preview Content"; 90 | sourceTree = ""; 91 | }; 92 | 572605072BAF5CA500F5F47A /* Views */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 5726050C2BAF5CFE00F5F47A /* HomeView.swift */, 96 | 572605172BAF84D800F5F47A /* FirstExpandedView.swift */, 97 | 570B4EC92BB0B99700D9B628 /* SecondExpandedView.swift */, 98 | 570B4ECB2BB0C34C00D9B628 /* ThirdExpandedView.swift */, 99 | ); 100 | path = Views; 101 | sourceTree = ""; 102 | }; 103 | 572605082BAF5CAD00F5F47A /* ViewModel */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 5726050A2BAF5CE600F5F47A /* HomeViewModel.swift */, 107 | ); 108 | path = ViewModel; 109 | sourceTree = ""; 110 | }; 111 | 572605092BAF5CB700F5F47A /* Managers */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 5726050E2BAF682F00F5F47A /* HapticsManager.swift */, 115 | 572605102BAF6E5D00F5F47A /* Colors.swift */, 116 | 572605122BAF6EAC00F5F47A /* BouncyButton.swift */, 117 | ); 118 | path = Managers; 119 | sourceTree = ""; 120 | }; 121 | 572605142BAF719100F5F47A /* Model */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 572605152BAF719B00F5F47A /* Model.swift */, 125 | ); 126 | path = Model; 127 | sourceTree = ""; 128 | }; 129 | /* End PBXGroup section */ 130 | 131 | /* Begin PBXNativeTarget section */ 132 | 572604F52BAF5A3800F5F47A /* ContinuityTransition */ = { 133 | isa = PBXNativeTarget; 134 | buildConfigurationList = 572605042BAF5A3A00F5F47A /* Build configuration list for PBXNativeTarget "ContinuityTransition" */; 135 | buildPhases = ( 136 | 572604F22BAF5A3800F5F47A /* Sources */, 137 | 572604F32BAF5A3800F5F47A /* Frameworks */, 138 | 572604F42BAF5A3800F5F47A /* Resources */, 139 | ); 140 | buildRules = ( 141 | ); 142 | dependencies = ( 143 | ); 144 | name = ContinuityTransition; 145 | productName = ContinuityTransition; 146 | productReference = 572604F62BAF5A3800F5F47A /* ContinuityTransition.app */; 147 | productType = "com.apple.product-type.application"; 148 | }; 149 | /* End PBXNativeTarget section */ 150 | 151 | /* Begin PBXProject section */ 152 | 572604EE2BAF5A3800F5F47A /* Project object */ = { 153 | isa = PBXProject; 154 | attributes = { 155 | BuildIndependentTargetsInParallel = 1; 156 | LastSwiftUpdateCheck = 1530; 157 | LastUpgradeCheck = 1530; 158 | TargetAttributes = { 159 | 572604F52BAF5A3800F5F47A = { 160 | CreatedOnToolsVersion = 15.3; 161 | }; 162 | }; 163 | }; 164 | buildConfigurationList = 572604F12BAF5A3800F5F47A /* Build configuration list for PBXProject "ContinuityTransition" */; 165 | compatibilityVersion = "Xcode 14.0"; 166 | developmentRegion = en; 167 | hasScannedForEncodings = 0; 168 | knownRegions = ( 169 | en, 170 | Base, 171 | ); 172 | mainGroup = 572604ED2BAF5A3800F5F47A; 173 | productRefGroup = 572604F72BAF5A3800F5F47A /* Products */; 174 | projectDirPath = ""; 175 | projectRoot = ""; 176 | targets = ( 177 | 572604F52BAF5A3800F5F47A /* ContinuityTransition */, 178 | ); 179 | }; 180 | /* End PBXProject section */ 181 | 182 | /* Begin PBXResourcesBuildPhase section */ 183 | 572604F42BAF5A3800F5F47A /* Resources */ = { 184 | isa = PBXResourcesBuildPhase; 185 | buildActionMask = 2147483647; 186 | files = ( 187 | 572605012BAF5A3A00F5F47A /* Preview Assets.xcassets in Resources */, 188 | 572604FE2BAF5A3A00F5F47A /* Assets.xcassets in Resources */, 189 | ); 190 | runOnlyForDeploymentPostprocessing = 0; 191 | }; 192 | /* End PBXResourcesBuildPhase section */ 193 | 194 | /* Begin PBXSourcesBuildPhase section */ 195 | 572604F22BAF5A3800F5F47A /* Sources */ = { 196 | isa = PBXSourcesBuildPhase; 197 | buildActionMask = 2147483647; 198 | files = ( 199 | 572604FC2BAF5A3800F5F47A /* ContentView.swift in Sources */, 200 | 572605132BAF6EAC00F5F47A /* BouncyButton.swift in Sources */, 201 | 572605112BAF6E5D00F5F47A /* Colors.swift in Sources */, 202 | 570B4ECA2BB0B99700D9B628 /* SecondExpandedView.swift in Sources */, 203 | 5726050D2BAF5CFE00F5F47A /* HomeView.swift in Sources */, 204 | 570B4ECC2BB0C34C00D9B628 /* ThirdExpandedView.swift in Sources */, 205 | 5726050B2BAF5CE600F5F47A /* HomeViewModel.swift in Sources */, 206 | 5726050F2BAF682F00F5F47A /* HapticsManager.swift in Sources */, 207 | 572604FA2BAF5A3800F5F47A /* ContinuityTransitionApp.swift in Sources */, 208 | 572605182BAF84D800F5F47A /* FirstExpandedView.swift in Sources */, 209 | 572605162BAF719B00F5F47A /* Model.swift in Sources */, 210 | ); 211 | runOnlyForDeploymentPostprocessing = 0; 212 | }; 213 | /* End PBXSourcesBuildPhase section */ 214 | 215 | /* Begin XCBuildConfiguration section */ 216 | 572605022BAF5A3A00F5F47A /* Debug */ = { 217 | isa = XCBuildConfiguration; 218 | buildSettings = { 219 | ALWAYS_SEARCH_USER_PATHS = NO; 220 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 221 | CLANG_ANALYZER_NONNULL = YES; 222 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 223 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 224 | CLANG_ENABLE_MODULES = YES; 225 | CLANG_ENABLE_OBJC_ARC = YES; 226 | CLANG_ENABLE_OBJC_WEAK = YES; 227 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 228 | CLANG_WARN_BOOL_CONVERSION = YES; 229 | CLANG_WARN_COMMA = YES; 230 | CLANG_WARN_CONSTANT_CONVERSION = YES; 231 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 232 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 233 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 234 | CLANG_WARN_EMPTY_BODY = YES; 235 | CLANG_WARN_ENUM_CONVERSION = YES; 236 | CLANG_WARN_INFINITE_RECURSION = YES; 237 | CLANG_WARN_INT_CONVERSION = YES; 238 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 239 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 240 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 241 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 242 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 243 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 244 | CLANG_WARN_STRICT_PROTOTYPES = YES; 245 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 246 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 247 | CLANG_WARN_UNREACHABLE_CODE = YES; 248 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 249 | COPY_PHASE_STRIP = NO; 250 | DEBUG_INFORMATION_FORMAT = dwarf; 251 | ENABLE_STRICT_OBJC_MSGSEND = YES; 252 | ENABLE_TESTABILITY = YES; 253 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 254 | GCC_C_LANGUAGE_STANDARD = gnu17; 255 | GCC_DYNAMIC_NO_PIC = NO; 256 | GCC_NO_COMMON_BLOCKS = YES; 257 | GCC_OPTIMIZATION_LEVEL = 0; 258 | GCC_PREPROCESSOR_DEFINITIONS = ( 259 | "DEBUG=1", 260 | "$(inherited)", 261 | ); 262 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 263 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 264 | GCC_WARN_UNDECLARED_SELECTOR = YES; 265 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 266 | GCC_WARN_UNUSED_FUNCTION = YES; 267 | GCC_WARN_UNUSED_VARIABLE = YES; 268 | IPHONEOS_DEPLOYMENT_TARGET = 17.4; 269 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 270 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 271 | MTL_FAST_MATH = YES; 272 | ONLY_ACTIVE_ARCH = YES; 273 | SDKROOT = iphoneos; 274 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; 275 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 276 | }; 277 | name = Debug; 278 | }; 279 | 572605032BAF5A3A00F5F47A /* Release */ = { 280 | isa = XCBuildConfiguration; 281 | buildSettings = { 282 | ALWAYS_SEARCH_USER_PATHS = NO; 283 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 284 | CLANG_ANALYZER_NONNULL = YES; 285 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 286 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 287 | CLANG_ENABLE_MODULES = YES; 288 | CLANG_ENABLE_OBJC_ARC = YES; 289 | CLANG_ENABLE_OBJC_WEAK = YES; 290 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 291 | CLANG_WARN_BOOL_CONVERSION = YES; 292 | CLANG_WARN_COMMA = YES; 293 | CLANG_WARN_CONSTANT_CONVERSION = YES; 294 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 295 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 296 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 297 | CLANG_WARN_EMPTY_BODY = YES; 298 | CLANG_WARN_ENUM_CONVERSION = YES; 299 | CLANG_WARN_INFINITE_RECURSION = YES; 300 | CLANG_WARN_INT_CONVERSION = YES; 301 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 302 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 303 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 304 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 305 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 306 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 307 | CLANG_WARN_STRICT_PROTOTYPES = YES; 308 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 309 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 310 | CLANG_WARN_UNREACHABLE_CODE = YES; 311 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 312 | COPY_PHASE_STRIP = NO; 313 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 314 | ENABLE_NS_ASSERTIONS = NO; 315 | ENABLE_STRICT_OBJC_MSGSEND = YES; 316 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 317 | GCC_C_LANGUAGE_STANDARD = gnu17; 318 | GCC_NO_COMMON_BLOCKS = YES; 319 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 320 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 321 | GCC_WARN_UNDECLARED_SELECTOR = YES; 322 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 323 | GCC_WARN_UNUSED_FUNCTION = YES; 324 | GCC_WARN_UNUSED_VARIABLE = YES; 325 | IPHONEOS_DEPLOYMENT_TARGET = 17.4; 326 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 327 | MTL_ENABLE_DEBUG_INFO = NO; 328 | MTL_FAST_MATH = YES; 329 | SDKROOT = iphoneos; 330 | SWIFT_COMPILATION_MODE = wholemodule; 331 | VALIDATE_PRODUCT = YES; 332 | }; 333 | name = Release; 334 | }; 335 | 572605052BAF5A3A00F5F47A /* Debug */ = { 336 | isa = XCBuildConfiguration; 337 | buildSettings = { 338 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 339 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 340 | CODE_SIGN_STYLE = Automatic; 341 | CURRENT_PROJECT_VERSION = 1; 342 | DEVELOPMENT_ASSET_PATHS = "\"ContinuityTransition/Preview Content\""; 343 | DEVELOPMENT_TEAM = F4WFB4363G; 344 | ENABLE_PREVIEWS = YES; 345 | GENERATE_INFOPLIST_FILE = YES; 346 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 347 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 348 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 349 | INFOPLIST_KEY_UIStatusBarStyle = ""; 350 | INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; 351 | IPHONEOS_DEPLOYMENT_TARGET = 17.1; 352 | LD_RUNPATH_SEARCH_PATHS = ( 353 | "$(inherited)", 354 | "@executable_path/Frameworks", 355 | ); 356 | MARKETING_VERSION = 1.0; 357 | PRODUCT_BUNDLE_IDENTIFIER = com.victor.ContinuityTransition; 358 | PRODUCT_NAME = "$(TARGET_NAME)"; 359 | SWIFT_EMIT_LOC_STRINGS = YES; 360 | SWIFT_VERSION = 5.0; 361 | TARGETED_DEVICE_FAMILY = "1,2"; 362 | }; 363 | name = Debug; 364 | }; 365 | 572605062BAF5A3A00F5F47A /* Release */ = { 366 | isa = XCBuildConfiguration; 367 | buildSettings = { 368 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 369 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 370 | CODE_SIGN_STYLE = Automatic; 371 | CURRENT_PROJECT_VERSION = 1; 372 | DEVELOPMENT_ASSET_PATHS = "\"ContinuityTransition/Preview Content\""; 373 | DEVELOPMENT_TEAM = F4WFB4363G; 374 | ENABLE_PREVIEWS = YES; 375 | GENERATE_INFOPLIST_FILE = YES; 376 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 377 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 378 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 379 | INFOPLIST_KEY_UIStatusBarStyle = ""; 380 | INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; 381 | IPHONEOS_DEPLOYMENT_TARGET = 17.1; 382 | LD_RUNPATH_SEARCH_PATHS = ( 383 | "$(inherited)", 384 | "@executable_path/Frameworks", 385 | ); 386 | MARKETING_VERSION = 1.0; 387 | PRODUCT_BUNDLE_IDENTIFIER = com.victor.ContinuityTransition; 388 | PRODUCT_NAME = "$(TARGET_NAME)"; 389 | SWIFT_EMIT_LOC_STRINGS = YES; 390 | SWIFT_VERSION = 5.0; 391 | TARGETED_DEVICE_FAMILY = "1,2"; 392 | }; 393 | name = Release; 394 | }; 395 | /* End XCBuildConfiguration section */ 396 | 397 | /* Begin XCConfigurationList section */ 398 | 572604F12BAF5A3800F5F47A /* Build configuration list for PBXProject "ContinuityTransition" */ = { 399 | isa = XCConfigurationList; 400 | buildConfigurations = ( 401 | 572605022BAF5A3A00F5F47A /* Debug */, 402 | 572605032BAF5A3A00F5F47A /* Release */, 403 | ); 404 | defaultConfigurationIsVisible = 0; 405 | defaultConfigurationName = Release; 406 | }; 407 | 572605042BAF5A3A00F5F47A /* Build configuration list for PBXNativeTarget "ContinuityTransition" */ = { 408 | isa = XCConfigurationList; 409 | buildConfigurations = ( 410 | 572605052BAF5A3A00F5F47A /* Debug */, 411 | 572605062BAF5A3A00F5F47A /* Release */, 412 | ); 413 | defaultConfigurationIsVisible = 0; 414 | defaultConfigurationName = Release; 415 | }; 416 | /* End XCConfigurationList section */ 417 | }; 418 | rootObject = 572604EE2BAF5A3800F5F47A /* Project object */; 419 | } 420 | -------------------------------------------------------------------------------- /ContinuityTransition.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ContinuityTransition.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ContinuityTransition.xcodeproj/xcuserdata/victor.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ContinuityTransition.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ContinuityTransition/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 | -------------------------------------------------------------------------------- /ContinuityTransition/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 | -------------------------------------------------------------------------------- /ContinuityTransition/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ContinuityTransition/Assets.xcassets/gray0.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "1.000", 9 | "green" : "1.000", 10 | "red" : "1.000" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ContinuityTransition/Assets.xcassets/gray100.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.961", 9 | "green" : "0.957", 10 | "red" : "0.957" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ContinuityTransition/Assets.xcassets/gray200.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.886", 9 | "green" : "0.882", 10 | "red" : "0.882" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ContinuityTransition/Assets.xcassets/gray300.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.831", 9 | "green" : "0.816", 10 | "red" : "0.816" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ContinuityTransition/Assets.xcassets/gray400.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.667", 9 | "green" : "0.631", 10 | "red" : "0.631" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ContinuityTransition/Assets.xcassets/gray50.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.988", 9 | "green" : "0.988", 10 | "red" : "0.988" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ContinuityTransition/Assets.xcassets/gray500.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.514", 9 | "green" : "0.478", 10 | "red" : "0.478" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ContinuityTransition/Assets.xcassets/gray600.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.357", 9 | "green" : "0.322", 10 | "red" : "0.322" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ContinuityTransition/Assets.xcassets/gray700.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.275", 9 | "green" : "0.247", 10 | "red" : "0.247" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ContinuityTransition/Assets.xcassets/gray800.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.165", 9 | "green" : "0.153", 10 | "red" : "0.153" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ContinuityTransition/Assets.xcassets/gray900.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.032", 9 | "green" : "0.028", 10 | "red" : "0.028" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ContinuityTransition/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // ContinuityTransition 4 | // 5 | // Created by Victor on 23/03/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ContentView: View { 11 | var body: some View { 12 | VStack { 13 | Image(systemName: "globe") 14 | .imageScale(.large) 15 | .foregroundStyle(.tint) 16 | Text("Hello, world!") 17 | } 18 | .padding() 19 | } 20 | } 21 | 22 | #Preview { 23 | ContentView() 24 | } 25 | -------------------------------------------------------------------------------- /ContinuityTransition/ContinuityTransitionApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContinuityTransitionApp.swift 3 | // ContinuityTransition 4 | // 5 | // Created by Victor on 23/03/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct ContinuityTransitionApp: App { 12 | 13 | @StateObject var viewModel = HomeViewModel() 14 | @Namespace var namespace 15 | 16 | var body: some Scene { 17 | WindowGroup { 18 | HomeView(namespace: namespace, expand: ExpandSection(title: "Send", description: "Send tokens or collectibles to any address or ENS username.", imageName: "paperplane.fill", backgroundColor: .blue)) 19 | .environmentObject(viewModel) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ContinuityTransition/Managers/BouncyButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BouncyButton.swift 3 | // ContinuityTransition 4 | // 5 | // Created by Victor on 23/03/2024. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct BouncyButton: ButtonStyle { 12 | public func makeBody(configuration: Self.Configuration) -> some View { 13 | return configuration.label 14 | .scaleEffect(x: configuration.isPressed ? 0.95 : 1.0, y: configuration.isPressed ? 0.9 : 1.0) 15 | .animation(.easeOut(duration: 0.2), value: configuration.isPressed) 16 | .opacity(configuration.isPressed ? 0.5 : 1) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ContinuityTransition/Managers/Colors.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Colors.swift 3 | // ContinuityTransition 4 | // 5 | // Created by Victor on 23/03/2024. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | extension Color { 12 | static let theme = ColorTheme() 13 | } 14 | 15 | struct ColorTheme { 16 | let gray900 = Color("gray900") 17 | let gray800 = Color("gray800") 18 | let gray700 = Color("gray700") 19 | let gray600 = Color("gray600") 20 | let gray500 = Color("gray500") 21 | let gray400 = Color("gray400") 22 | let gray300 = Color("gray300") 23 | let gray200 = Color("gray200") 24 | let gray100 = Color("gray100") 25 | let gray50 = Color("gray50") 26 | let gray0 = Color("gray0") 27 | } 28 | -------------------------------------------------------------------------------- /ContinuityTransition/Managers/HapticsManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HapticsManager.swift 3 | // ContinuityTransition 4 | // 5 | // Created by Victor on 23/03/2024. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | import SwiftUI 11 | 12 | 13 | class HapticManager: ObservableObject { 14 | static let instance = HapticManager() //This is a singleton 15 | 16 | func notification(type: UINotificationFeedbackGenerator.FeedbackType) { 17 | let generator = UINotificationFeedbackGenerator() 18 | generator.notificationOccurred(type) 19 | 20 | } 21 | 22 | func impact(style: UIImpactFeedbackGenerator.FeedbackStyle) { 23 | let generator = UIImpactFeedbackGenerator(style: style) 24 | generator.impactOccurred() 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /ContinuityTransition/Model/Model.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Model.swift 3 | // ContinuityTransition 4 | // 5 | // Created by Victor on 23/03/2024. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct ExpandSection: Identifiable { 12 | let id = UUID() 13 | let title: String 14 | let description: String 15 | let imageName: String 16 | let backgroundColor: Color 17 | } 18 | -------------------------------------------------------------------------------- /ContinuityTransition/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ContinuityTransition/ViewModel/HomeViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeViewModel.swift 3 | // ContinuityTransition 4 | // 5 | // Created by Victor on 23/03/2024. 6 | // 7 | 8 | import Foundation 9 | 10 | class HomeViewModel: ObservableObject { 11 | @Published var moveItems: Bool = false 12 | @Published var showItems: Bool = false 13 | @Published var showNextView: Bool = false 14 | @Published var selectedExpandIndex: Int? = nil 15 | } 16 | -------------------------------------------------------------------------------- /ContinuityTransition/Views/FirstExpandedView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FirstExpandedView.swift 3 | // ContinuityTransition 4 | // 5 | // Created by Victor on 23/03/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct FirstExpandedView: View { 11 | 12 | var namespace: Namespace.ID 13 | @State private var textField: String = "" 14 | @State private var appear: Bool = false 15 | @EnvironmentObject var viewModel: HomeViewModel 16 | 17 | var body: some View { 18 | ZStack { 19 | Color.gray900.ignoresSafeArea() 20 | ZStack(alignment: .bottom) { 21 | VStack(alignment: .leading, spacing: 16) { 22 | HStack { 23 | Text("Book") 24 | .font(.title) 25 | .fontWeight(.bold) 26 | .foregroundStyle(.gray0) 27 | 28 | Spacer() 29 | Button(action: { 30 | withAnimation(.spring(response: 0.25, dampingFraction: 0.85)) { 31 | viewModel.showItems = true 32 | HapticManager.instance.impact(style: .light) 33 | viewModel.selectedExpandIndex = nil 34 | } 35 | withAnimation(.spring(response: 0.28, dampingFraction: 0.9)) { 36 | viewModel.moveItems = false 37 | } 38 | }, label: { 39 | Image(systemName: "xmark") 40 | .renderingMode(.template) 41 | .fontWeight(.semibold) 42 | .foregroundColor(.gray100) 43 | .padding(12) 44 | .background(.gray800) 45 | .cornerRadius(32) 46 | }) 47 | } 48 | 49 | ScrollView { 50 | VStack(alignment: .leading, spacing: 8) { 51 | ForEach(0..<20) { index in 52 | Rectangle() 53 | .foregroundStyle(.gray800.opacity(0.6)) 54 | .frame(width: .random(in: 200..<350), alignment: .leading) 55 | .frame(height: .random(in: 40..<50), alignment: .leading) 56 | .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) 57 | 58 | } 59 | } 60 | } 61 | .scrollIndicators(.hidden) 62 | } 63 | .padding(.horizontal, 20) 64 | .padding(.bottom, 16) 65 | .padding(.top, 80) 66 | 67 | 68 | Button { 69 | 70 | } label: { 71 | Rectangle() 72 | .foregroundStyle(.gray0) 73 | .frame(width: 300, height: 56, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/) 74 | .clipShape(RoundedRectangle(cornerRadius: 40, style: .continuous)) 75 | .padding(48) 76 | } 77 | .buttonStyle(BouncyButton()) 78 | } 79 | .scaleEffect(appear ? 1 : 0.5, anchor: .bottom) 80 | .opacity(appear ? 1 : 0) 81 | .transition(.scale(scale: 1)) // this transition is kept to remove the default opacity fade when using a matchedGeometryEffect. 82 | } 83 | .fontDesign(.rounded) 84 | .background( 85 | RoundedRectangle(cornerRadius: 24, style: .continuous) 86 | .fill(.gray900) 87 | .matchedGeometryEffect(id: "background", in: namespace) 88 | ) 89 | .mask({ 90 | RoundedRectangle(cornerRadius: 24, style: .continuous) 91 | .matchedGeometryEffect(id: "mask", in: namespace) 92 | 93 | }) 94 | .ignoresSafeArea(.all) 95 | 96 | .onAppear(perform: { 97 | withAnimation(.spring(response: 0.3, dampingFraction: 0.85, blendDuration: 1)) { 98 | appear = true 99 | } 100 | }) 101 | } 102 | } 103 | 104 | struct FirstExpandedView_Preview: PreviewProvider { 105 | @Namespace static var namespace 106 | 107 | static var previews: some View { 108 | FirstExpandedView(namespace: namespace) 109 | .environmentObject(HomeViewModel()) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /ContinuityTransition/Views/HomeView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeView.swift 3 | // ContinuityTransition 4 | // 5 | // Created by Victor on 23/03/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct HomeView: View { 11 | 12 | @EnvironmentObject var viewModel: HomeViewModel 13 | @State private var show: Bool = false 14 | @State private var appear: Bool = true 15 | @State private var selectedExpandIndex: Int? = nil 16 | var namespace: Namespace.ID 17 | 18 | let section: [ExpandSection] = [ 19 | ExpandSection(title: "Book", description: "Book flights, hotels, and activities for your next adventure.", imageName: "airplane", backgroundColor: .orange), 20 | 21 | ExpandSection(title: "Explore", description: "Discover new destinations and plan your itinerary.", imageName: "map.fill", backgroundColor: .pink), 22 | 23 | ExpandSection(title: "Stay", description: "Find the perfect accommodation for a comfortable stay.", imageName: "house.fill", backgroundColor: .teal), 24 | ] 25 | 26 | let expand: ExpandSection 27 | 28 | var body: some View { 29 | 30 | ZStack(alignment: .bottomTrailing) { 31 | lowFidelity 32 | 33 | // This controls navigation to other views. Currently there are 4 main views, you can add more if you want. 34 | if viewModel.selectedExpandIndex != nil { 35 | if let index = viewModel.selectedExpandIndex { 36 | switch index { 37 | case 0: 38 | FirstExpandedView(namespace: namespace) 39 | case 1: 40 | SecondExpandedView(namespace: namespace) 41 | case 2: 42 | ThirdExpandedView(namespace: namespace) 43 | default: 44 | HomeView(namespace: namespace, expand: ExpandSection(title: "", description: "", imageName: "", backgroundColor: .clear)) 45 | } 46 | } 47 | } else { 48 | ZStack(content: { 49 | combinedViews 50 | .padding(20) 51 | //.offset(y: viewModel.showItems ? 0 : -80) 52 | }) 53 | .transition(.scale(scale: 1)) 54 | } 55 | } 56 | } 57 | } 58 | 59 | 60 | struct HomeView_Preview: PreviewProvider { 61 | @Namespace static var namespace 62 | 63 | static var previews: some View { 64 | HomeView(namespace: namespace, expand: ExpandSection(title: "Send", description: "Send tokens or collectibles to any address or ENS username.", imageName: "paperplane.fill", backgroundColor: .blue)) 65 | .environmentObject(HomeViewModel()) 66 | } 67 | } 68 | 69 | struct ExpandView: View { 70 | let expand: ExpandSection 71 | 72 | var body: some View { 73 | HStack(alignment: .top, spacing: 16) { 74 | Image(systemName: expand.imageName) 75 | .font(.body) 76 | .fontWeight(.semibold) 77 | .padding(8) 78 | .foregroundStyle(.gray0) 79 | .background(expand.backgroundColor.gradient) 80 | .clipShape(Circle()) 81 | 82 | VStack(alignment: .leading, spacing: 4) { 83 | Text(expand.title) 84 | .font(.title3) 85 | .fontWeight(.bold) 86 | .foregroundStyle(.gray0) 87 | 88 | Text(expand.description) 89 | .font(.callout) 90 | .foregroundStyle(.gray300) 91 | } 92 | } 93 | .fontDesign(.rounded) 94 | .padding(16) 95 | .frame(width: 336, alignment: .leading) 96 | .background(.gray800.opacity(0.6)) 97 | .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) 98 | .overlay { 99 | RoundedRectangle(cornerRadius: 16, style: .continuous) 100 | .stroke(Color.gray700, lineWidth: 0.7) 101 | } 102 | } 103 | } 104 | 105 | 106 | extension HomeView { 107 | var plusButton: some View { 108 | Button { 109 | HapticManager.instance.impact(style: .light) 110 | 111 | withAnimation(.spring(response: 0.25, dampingFraction: 0.85, blendDuration: 1)) { 112 | viewModel.showItems = true 113 | } 114 | 115 | } label: { 116 | Image(systemName: "plus") 117 | .font(.title) 118 | .fontWeight(.semibold) 119 | .foregroundStyle(.gray0) 120 | .padding(16) 121 | } 122 | 123 | } 124 | 125 | var combinedViews: some View { 126 | ZStack { 127 | if !viewModel.showItems { 128 | plusButton 129 | } 130 | else { 131 | VStack(alignment: .leading, spacing: 8) { 132 | if !viewModel.showNextView { 133 | ZStack { 134 | VStack(alignment: .leading, spacing: 8, content: { 135 | ForEach(section.indices, id: \.self) { index in 136 | Button { 137 | withAnimation(.spring(response: 0.25, dampingFraction: 0.85)) { 138 | viewModel.selectedExpandIndex = index 139 | viewModel.moveItems = true 140 | } 141 | 142 | } label: { 143 | ExpandView(expand: section[index]) 144 | } 145 | .buttonStyle(BouncyButton()) 146 | } 147 | }) 148 | } 149 | } 150 | } 151 | .offset(y: viewModel.moveItems ? -200 : 0) 152 | .opacity(viewModel.moveItems ? 0 : 1) 153 | } 154 | } 155 | .padding(viewModel.showItems ? 8 : 0) 156 | .background( 157 | RoundedRectangle(cornerRadius: viewModel.showItems ? 24 : 32, style: .continuous) 158 | .fill(.gray900) 159 | .matchedGeometryEffect(id: "background", in: namespace) 160 | ) 161 | .mask({ 162 | RoundedRectangle(cornerRadius: viewModel.showItems ? 24 : 32, style: .continuous) 163 | .matchedGeometryEffect(id: "mask", in: namespace) 164 | }) 165 | } 166 | 167 | var lowFidelity: some View { 168 | ZStack(alignment: .bottom, content: { 169 | ScrollView { 170 | VStack(alignment: .center, spacing: 24) { 171 | HStack(alignment: .center, spacing: 24, content: { 172 | ForEach(0..<2) { index in 173 | Rectangle() 174 | .foregroundStyle(.gray100) 175 | .frame(width: 70, height: 40) 176 | .clipShape(RoundedRectangle(cornerRadius: 24, style: .continuous)) 177 | } 178 | }) 179 | 180 | HStack(alignment: .top, spacing: 8) { 181 | VStack(alignment: .center, spacing: 16, content: { 182 | ForEach(0..<7) { index in 183 | Rectangle() 184 | .foregroundStyle(.gray100) 185 | .frame(width: 175, height: 250) 186 | .clipShape(RoundedRectangle(cornerRadius: 24, style: .continuous)) 187 | } 188 | }) 189 | 190 | VStack(alignment: .center, spacing: 16, content: { 191 | ForEach(0..<10) { index in 192 | Rectangle() 193 | .foregroundStyle(.gray100) 194 | .frame(width: 175, height: 156) 195 | .clipShape(RoundedRectangle(cornerRadius: 24, style: .continuous)) 196 | } 197 | }) 198 | 199 | } 200 | } 201 | .padding(.top, 24) 202 | } 203 | .scrollIndicators(.hidden) 204 | .scaleEffect(viewModel.moveItems ? 0.8 : 1, anchor: .bottom) 205 | }) 206 | .padding(.horizontal, 20) 207 | .background(.gray0) 208 | .onTapGesture { 209 | withAnimation(.spring(response: 0.25, dampingFraction: 0.85, blendDuration: 1)) { 210 | viewModel.showItems = false 211 | } 212 | } 213 | 214 | } 215 | } 216 | 217 | 218 | -------------------------------------------------------------------------------- /ContinuityTransition/Views/SecondExpandedView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SecondExpandedView.swift 3 | // ContinuityTransition 4 | // 5 | // Created by Victor on 24/03/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct SecondExpandedView: View { 11 | 12 | var namespace: Namespace.ID 13 | @State private var textField: String = "" 14 | @State private var appear: Bool = false 15 | @EnvironmentObject var viewModel: HomeViewModel 16 | 17 | let columns: [GridItem] = [ 18 | GridItem(.flexible(), spacing: 16), 19 | GridItem(.flexible(), spacing: 16) 20 | ] 21 | 22 | var body: some View { 23 | ZStack { 24 | Color.gray900.ignoresSafeArea() 25 | VStack(alignment: .center, spacing: 16) { 26 | HStack { 27 | Text("Explore") 28 | .font(.title) 29 | .fontWeight(.bold) 30 | .foregroundStyle(.gray0) 31 | Spacer() 32 | Button(action: { 33 | withAnimation(.spring(response: 0.25, dampingFraction: 0.85)) { 34 | viewModel.showItems = true 35 | HapticManager.instance.impact(style: .light) 36 | viewModel.selectedExpandIndex = nil 37 | } 38 | withAnimation(.spring(response: 0.28, dampingFraction: 0.9)) { 39 | viewModel.moveItems = false 40 | } 41 | }, label: { 42 | Image(systemName: "xmark") 43 | .renderingMode(.template) 44 | .fontWeight(.semibold) 45 | .foregroundColor(.gray100) 46 | .padding(12) 47 | .background(.gray800) 48 | .cornerRadius(32) 49 | }) 50 | } 51 | 52 | ScrollView(.vertical, showsIndicators: false) { 53 | LazyVGrid(columns: columns, spacing: 16) { 54 | ForEach(0..<12) { index in 55 | Rectangle() 56 | .foregroundStyle(.gray800.opacity(0.6)) 57 | .frame(height: 150, alignment: .center) 58 | .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) 59 | } 60 | } 61 | } 62 | .scrollDisabled(false) 63 | } 64 | .padding(.horizontal, 20) 65 | .padding(.bottom, 16) 66 | .padding(.top, 80) 67 | .scaleEffect(appear ? 1 : 0.5, anchor: .bottom) 68 | .opacity(appear ? 1 : 0) 69 | .transition(.scale(scale: 1)) // this transition is kept to remove the default opacity fade when using a matchedGeometryEffect. 70 | 71 | } 72 | .fontDesign(.rounded) 73 | .background( 74 | RoundedRectangle(cornerRadius: 24, style: .continuous) 75 | .fill(.gray900) 76 | .matchedGeometryEffect(id: "background", in: namespace) 77 | ) 78 | .mask({ 79 | RoundedRectangle(cornerRadius: 24, style: .continuous) 80 | .matchedGeometryEffect(id: "mask", in: namespace) 81 | 82 | }) 83 | .ignoresSafeArea(.all) 84 | .onAppear(perform: { 85 | withAnimation(.spring(response: 0.3, dampingFraction: 0.85, blendDuration: 1)) { 86 | appear = true 87 | } 88 | }) 89 | } 90 | } 91 | 92 | struct SecondExpandedView_Preview: PreviewProvider { 93 | @Namespace static var namespace 94 | 95 | static var previews: some View { 96 | SecondExpandedView(namespace: namespace) 97 | .environmentObject(HomeViewModel()) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /ContinuityTransition/Views/ThirdExpandedView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThirdExpandedView.swift 3 | // ContinuityTransition 4 | // 5 | // Created by Victor on 24/03/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ThirdExpandedView: View { 11 | 12 | var namespace: Namespace.ID 13 | @State private var textField: String = "" 14 | @State private var appear: Bool = false 15 | @EnvironmentObject var viewModel: HomeViewModel 16 | 17 | let columns: [GridItem] = [ 18 | GridItem(.flexible(), spacing: 16), 19 | GridItem(.flexible(), spacing: 16) 20 | ] 21 | 22 | var body: some View { 23 | ZStack { 24 | Color.gray900.ignoresSafeArea() 25 | VStack { 26 | VStack(alignment: .leading, spacing: 16) { 27 | HStack { 28 | Text("Stay") 29 | .font(.title) 30 | .fontWeight(.bold) 31 | .foregroundStyle(.gray0) 32 | 33 | Spacer() 34 | Button(action: { 35 | withAnimation(.spring(response: 0.25, dampingFraction: 0.85)) { 36 | viewModel.showItems = true 37 | HapticManager.instance.impact(style: .light) 38 | viewModel.selectedExpandIndex = nil 39 | } 40 | withAnimation(.spring(response: 0.28, dampingFraction: 0.9)) { 41 | viewModel.moveItems = false 42 | } 43 | }, label: { 44 | Image(systemName: "xmark") 45 | .renderingMode(.template) 46 | .fontWeight(.semibold) 47 | .foregroundColor(.gray100) 48 | .padding(12) 49 | .background(.gray800) 50 | .cornerRadius(32) 51 | }) 52 | } 53 | 54 | ScrollView { 55 | VStack(alignment: .leading, spacing: 16) { 56 | Rectangle() 57 | .foregroundStyle(.gray800.opacity(0.6)) 58 | .frame(height: 180) 59 | .frame(maxWidth: .infinity) 60 | .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) 61 | 62 | LazyVGrid(columns: columns, spacing: 16) { 63 | ForEach(0..<4) { index in 64 | Rectangle() 65 | .foregroundStyle(.gray800.opacity(0.6)) 66 | .frame(height: 150, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/) 67 | .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) 68 | } 69 | } 70 | 71 | Rectangle() 72 | .foregroundStyle(.gray800.opacity(0.6)) 73 | .frame(height: 180) 74 | .frame(maxWidth: .infinity) 75 | .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) 76 | } 77 | } 78 | .scrollIndicators(.hidden) 79 | } 80 | 81 | Spacer() 82 | Button { 83 | withAnimation(.spring(response: 0.35, dampingFraction: 0.89)) { 84 | 85 | } 86 | 87 | } label: { 88 | 89 | } 90 | .buttonStyle(BouncyButton()) 91 | 92 | } 93 | .padding(.horizontal, 20) 94 | .padding(.bottom, 16) 95 | .padding(.top, 80) 96 | .scaleEffect(appear ? 1 : 0.5, anchor: .bottom) 97 | .opacity(appear ? 1 : 0) 98 | .transition(.scale(scale: 1)) // this transition is kept to remove the default opacity fade when using a matchedGeometryEffect. 99 | 100 | } 101 | .fontDesign(.rounded) 102 | .background( 103 | RoundedRectangle(cornerRadius: 24, style: .continuous) 104 | .fill(.gray900) 105 | .matchedGeometryEffect(id: "background", in: namespace) 106 | ) 107 | .mask({ 108 | RoundedRectangle(cornerRadius: 24, style: .continuous) 109 | .matchedGeometryEffect(id: "mask", in: namespace) 110 | 111 | }) 112 | .ignoresSafeArea(.all) 113 | .onAppear(perform: { 114 | withAnimation(.spring(response: 0.3, dampingFraction: 0.85, blendDuration: 1)) { 115 | appear = true 116 | } 117 | }) 118 | 119 | } 120 | } 121 | 122 | struct ThirdExpandedView_Preview: PreviewProvider { 123 | @Namespace static var namespace 124 | 125 | static var previews: some View { 126 | ThirdExpandedView(namespace: namespace) 127 | .environmentObject(HomeViewModel()) 128 | } 129 | } 130 | 131 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | I tried recreating a familiar continuity transition from the Family mobile app in Swift UI. This transition creates a sense of flow and connection between different views in the app. 2 | 3 | ![screen](https://github.com/zurikss/ContinuityTransition/assets/47340237/32a3c5f9-13da-4c46-bf46-b44ca3aec28d) 4 | --------------------------------------------------------------------------------