├── .gitattributes ├── OnboardingAnimationOne.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── boris.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── boris.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── OnboardingAnimationOne ├── .DS_Store ├── App │ ├── .DS_Store │ ├── ContentView.swift │ ├── OnboardingAnimationOneApp.swift │ └── Views │ │ ├── .DS_Store │ │ ├── Common │ │ ├── AdaptivePagingScrollView.swift │ │ ├── FrameModifier.swift │ │ └── Orientation.swift │ │ ├── Onboarding │ │ ├── .DS_Store │ │ ├── Elements │ │ │ ├── OnbardingCardView.swift │ │ │ ├── OnboardingContinueButton.swift │ │ │ └── PageControl.swift │ │ ├── Model │ │ │ ├── OnboardingCard.swift │ │ │ ├── OnboardingCardsData.swift │ │ │ └── cards.json │ │ └── OnboardingView.swift │ │ └── Units │ │ ├── Elements │ │ └── UnitView.swift │ │ ├── Model │ │ ├── UnitRow.swift │ │ └── UnitRowsData.swift │ │ └── UnitsView.swift ├── Extensions │ ├── Bundle+Ext.swift │ ├── Color+Ext.swift │ └── View+Ext.swift ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json └── Resourсes │ └── Assets.xcassets │ ├── App │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── Card Images │ ├── Contents.json │ ├── card_image_01.imageset │ │ ├── Contents.json │ │ └── Illustration 1.svg │ ├── card_image_02.imageset │ │ ├── Contents.json │ │ └── Illustration 2.svg │ └── card_image_03.imageset │ │ ├── Contents.json │ │ └── Illustration 3.svg │ └── Contents.json ├── README.md └── example.gif /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /OnboardingAnimationOne.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 55; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4455A46B2A673A9C00849DB8 /* UnitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4455A46A2A673A9C00849DB8 /* UnitView.swift */; }; 11 | 4455A46F2A673AF700849DB8 /* UnitRowsData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4455A46E2A673AF700849DB8 /* UnitRowsData.swift */; }; 12 | 4455A4712A673B0000849DB8 /* UnitRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4455A4702A673B0000849DB8 /* UnitRow.swift */; }; 13 | 4455A4742A673B9000849DB8 /* Orientation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4455A4732A673B9000849DB8 /* Orientation.swift */; }; 14 | 4455A4762A6861AD00849DB8 /* FrameModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4455A4752A6861AD00849DB8 /* FrameModifier.swift */; }; 15 | 4455A4782A6861CF00849DB8 /* View+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4455A4772A6861CF00849DB8 /* View+Ext.swift */; }; 16 | 448834AF2A67223F00B87509 /* UnitsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 448834AE2A67223F00B87509 /* UnitsView.swift */; }; 17 | A1390E6E28C073FE007F5BA1 /* OnboardingAnimationOneApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1390E6D28C073FE007F5BA1 /* OnboardingAnimationOneApp.swift */; }; 18 | A1390E7028C073FE007F5BA1 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1390E6F28C073FE007F5BA1 /* ContentView.swift */; }; 19 | A1390E7228C073FF007F5BA1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A1390E7128C073FF007F5BA1 /* Assets.xcassets */; }; 20 | A1390E7528C073FF007F5BA1 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A1390E7428C073FF007F5BA1 /* Preview Assets.xcassets */; }; 21 | A1390E9828C07818007F5BA1 /* Bundle+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1390E9728C07818007F5BA1 /* Bundle+Ext.swift */; }; 22 | A1390E9A28C07853007F5BA1 /* cards.json in Resources */ = {isa = PBXBuildFile; fileRef = A1390E9928C07853007F5BA1 /* cards.json */; }; 23 | A1D4CE2028C6501E00D75AE1 /* OnboardingCardsData.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D4CE1F28C6501E00D75AE1 /* OnboardingCardsData.swift */; }; 24 | A1D4CE2428C712E600D75AE1 /* OnbardingCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D4CE2328C712E600D75AE1 /* OnbardingCardView.swift */; }; 25 | A1D4CE2628C7384800D75AE1 /* AdaptivePagingScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D4CE2528C7384800D75AE1 /* AdaptivePagingScrollView.swift */; }; 26 | A1D4CE2828C8555500D75AE1 /* PageControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D4CE2728C8555500D75AE1 /* PageControl.swift */; }; 27 | A1D4CE2A28C86B4100D75AE1 /* Color+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D4CE2928C86B4100D75AE1 /* Color+Ext.swift */; }; 28 | A1D4CE2C28C86B5A00D75AE1 /* OnboardingContinueButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D4CE2B28C86B5A00D75AE1 /* OnboardingContinueButton.swift */; }; 29 | A1D4CE3328C89DD700D75AE1 /* OnboardingCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D4CE3228C89DD700D75AE1 /* OnboardingCard.swift */; }; 30 | A1D4CE3528C89E0C00D75AE1 /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D4CE3428C89E0C00D75AE1 /* OnboardingView.swift */; }; 31 | /* End PBXBuildFile section */ 32 | 33 | /* Begin PBXContainerItemProxy section */ 34 | A1390E7B28C073FF007F5BA1 /* PBXContainerItemProxy */ = { 35 | isa = PBXContainerItemProxy; 36 | containerPortal = A1390E6228C073FE007F5BA1 /* Project object */; 37 | proxyType = 1; 38 | remoteGlobalIDString = A1390E6928C073FE007F5BA1; 39 | remoteInfo = OnboardingAnimationOne; 40 | }; 41 | A1390E8528C073FF007F5BA1 /* PBXContainerItemProxy */ = { 42 | isa = PBXContainerItemProxy; 43 | containerPortal = A1390E6228C073FE007F5BA1 /* Project object */; 44 | proxyType = 1; 45 | remoteGlobalIDString = A1390E6928C073FE007F5BA1; 46 | remoteInfo = OnboardingAnimationOne; 47 | }; 48 | /* End PBXContainerItemProxy section */ 49 | 50 | /* Begin PBXFileReference section */ 51 | 4455A46A2A673A9C00849DB8 /* UnitView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitView.swift; sourceTree = ""; }; 52 | 4455A46E2A673AF700849DB8 /* UnitRowsData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitRowsData.swift; sourceTree = ""; }; 53 | 4455A4702A673B0000849DB8 /* UnitRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitRow.swift; sourceTree = ""; }; 54 | 4455A4732A673B9000849DB8 /* Orientation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Orientation.swift; sourceTree = ""; }; 55 | 4455A4752A6861AD00849DB8 /* FrameModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FrameModifier.swift; sourceTree = ""; }; 56 | 4455A4772A6861CF00849DB8 /* View+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Ext.swift"; sourceTree = ""; }; 57 | 448834AE2A67223F00B87509 /* UnitsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitsView.swift; sourceTree = ""; }; 58 | A1390E6A28C073FE007F5BA1 /* OnboardingAnimationOne.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OnboardingAnimationOne.app; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | A1390E6D28C073FE007F5BA1 /* OnboardingAnimationOneApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingAnimationOneApp.swift; sourceTree = ""; }; 60 | A1390E6F28C073FE007F5BA1 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 61 | A1390E7128C073FF007F5BA1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 62 | A1390E7428C073FF007F5BA1 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 63 | A1390E7A28C073FF007F5BA1 /* OnboardingAnimationOneTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OnboardingAnimationOneTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 64 | A1390E8428C073FF007F5BA1 /* OnboardingAnimationOneUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OnboardingAnimationOneUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 65 | A1390E9728C07818007F5BA1 /* Bundle+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+Ext.swift"; sourceTree = ""; }; 66 | A1390E9928C07853007F5BA1 /* cards.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = cards.json; sourceTree = ""; }; 67 | A1D4CE1F28C6501E00D75AE1 /* OnboardingCardsData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingCardsData.swift; sourceTree = ""; }; 68 | A1D4CE2328C712E600D75AE1 /* OnbardingCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnbardingCardView.swift; sourceTree = ""; }; 69 | A1D4CE2528C7384800D75AE1 /* AdaptivePagingScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdaptivePagingScrollView.swift; sourceTree = ""; }; 70 | A1D4CE2728C8555500D75AE1 /* PageControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageControl.swift; sourceTree = ""; }; 71 | A1D4CE2928C86B4100D75AE1 /* Color+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Ext.swift"; sourceTree = ""; }; 72 | A1D4CE2B28C86B5A00D75AE1 /* OnboardingContinueButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingContinueButton.swift; sourceTree = ""; }; 73 | A1D4CE3228C89DD700D75AE1 /* OnboardingCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingCard.swift; sourceTree = ""; }; 74 | A1D4CE3428C89E0C00D75AE1 /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = ""; }; 75 | /* End PBXFileReference section */ 76 | 77 | /* Begin PBXFrameworksBuildPhase section */ 78 | A1390E6728C073FE007F5BA1 /* Frameworks */ = { 79 | isa = PBXFrameworksBuildPhase; 80 | buildActionMask = 2147483647; 81 | files = ( 82 | ); 83 | runOnlyForDeploymentPostprocessing = 0; 84 | }; 85 | A1390E7728C073FF007F5BA1 /* Frameworks */ = { 86 | isa = PBXFrameworksBuildPhase; 87 | buildActionMask = 2147483647; 88 | files = ( 89 | ); 90 | runOnlyForDeploymentPostprocessing = 0; 91 | }; 92 | A1390E8128C073FF007F5BA1 /* Frameworks */ = { 93 | isa = PBXFrameworksBuildPhase; 94 | buildActionMask = 2147483647; 95 | files = ( 96 | ); 97 | runOnlyForDeploymentPostprocessing = 0; 98 | }; 99 | /* End PBXFrameworksBuildPhase section */ 100 | 101 | /* Begin PBXGroup section */ 102 | 4455A46C2A673AC800849DB8 /* Elements */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 4455A46A2A673A9C00849DB8 /* UnitView.swift */, 106 | ); 107 | path = Elements; 108 | sourceTree = ""; 109 | }; 110 | 4455A46D2A673AD800849DB8 /* Model */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 4455A46E2A673AF700849DB8 /* UnitRowsData.swift */, 114 | 4455A4702A673B0000849DB8 /* UnitRow.swift */, 115 | ); 116 | path = Model; 117 | sourceTree = ""; 118 | }; 119 | 4455A4722A673B8000849DB8 /* Common */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | A1D4CE2528C7384800D75AE1 /* AdaptivePagingScrollView.swift */, 123 | 4455A4732A673B9000849DB8 /* Orientation.swift */, 124 | 4455A4752A6861AD00849DB8 /* FrameModifier.swift */, 125 | ); 126 | path = Common; 127 | sourceTree = ""; 128 | }; 129 | 448834AD2A67222C00B87509 /* Units */ = { 130 | isa = PBXGroup; 131 | children = ( 132 | 4455A46D2A673AD800849DB8 /* Model */, 133 | 4455A46C2A673AC800849DB8 /* Elements */, 134 | 448834AE2A67223F00B87509 /* UnitsView.swift */, 135 | ); 136 | path = Units; 137 | sourceTree = ""; 138 | }; 139 | A1390E6128C073FE007F5BA1 = { 140 | isa = PBXGroup; 141 | children = ( 142 | A1390E6C28C073FE007F5BA1 /* OnboardingAnimationOne */, 143 | A1390E6B28C073FE007F5BA1 /* Products */, 144 | ); 145 | sourceTree = ""; 146 | }; 147 | A1390E6B28C073FE007F5BA1 /* Products */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | A1390E6A28C073FE007F5BA1 /* OnboardingAnimationOne.app */, 151 | A1390E7A28C073FF007F5BA1 /* OnboardingAnimationOneTests.xctest */, 152 | A1390E8428C073FF007F5BA1 /* OnboardingAnimationOneUITests.xctest */, 153 | ); 154 | name = Products; 155 | sourceTree = ""; 156 | }; 157 | A1390E6C28C073FE007F5BA1 /* OnboardingAnimationOne */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | A1390E9D28C2074C007F5BA1 /* App */, 161 | A1390E9C28C20722007F5BA1 /* Extensions */, 162 | A1390E9B28C20715007F5BA1 /* Resourсes */, 163 | A1390E7328C073FF007F5BA1 /* Preview Content */, 164 | ); 165 | path = OnboardingAnimationOne; 166 | sourceTree = ""; 167 | }; 168 | A1390E7328C073FF007F5BA1 /* Preview Content */ = { 169 | isa = PBXGroup; 170 | children = ( 171 | A1390E7428C073FF007F5BA1 /* Preview Assets.xcassets */, 172 | ); 173 | path = "Preview Content"; 174 | sourceTree = ""; 175 | }; 176 | A1390E9B28C20715007F5BA1 /* Resourсes */ = { 177 | isa = PBXGroup; 178 | children = ( 179 | A1390E7128C073FF007F5BA1 /* Assets.xcassets */, 180 | ); 181 | path = "Resourсes"; 182 | sourceTree = ""; 183 | }; 184 | A1390E9C28C20722007F5BA1 /* Extensions */ = { 185 | isa = PBXGroup; 186 | children = ( 187 | A1D4CE2928C86B4100D75AE1 /* Color+Ext.swift */, 188 | 4455A4772A6861CF00849DB8 /* View+Ext.swift */, 189 | A1390E9728C07818007F5BA1 /* Bundle+Ext.swift */, 190 | ); 191 | path = Extensions; 192 | sourceTree = ""; 193 | }; 194 | A1390E9D28C2074C007F5BA1 /* App */ = { 195 | isa = PBXGroup; 196 | children = ( 197 | A1390E6D28C073FE007F5BA1 /* OnboardingAnimationOneApp.swift */, 198 | A1390E6F28C073FE007F5BA1 /* ContentView.swift */, 199 | A1D4CE2D28C86D2A00D75AE1 /* Views */, 200 | ); 201 | path = App; 202 | sourceTree = ""; 203 | }; 204 | A1D4CE2D28C86D2A00D75AE1 /* Views */ = { 205 | isa = PBXGroup; 206 | children = ( 207 | 4455A4722A673B8000849DB8 /* Common */, 208 | 448834AD2A67222C00B87509 /* Units */, 209 | A1D4CE2E28C86D4800D75AE1 /* Onboarding */, 210 | ); 211 | path = Views; 212 | sourceTree = ""; 213 | }; 214 | A1D4CE2E28C86D4800D75AE1 /* Onboarding */ = { 215 | isa = PBXGroup; 216 | children = ( 217 | A1D4CE3428C89E0C00D75AE1 /* OnboardingView.swift */, 218 | A1D4CE3028C86D7A00D75AE1 /* Model */, 219 | A1D4CE2F28C86D6500D75AE1 /* Elements */, 220 | ); 221 | path = Onboarding; 222 | sourceTree = ""; 223 | }; 224 | A1D4CE2F28C86D6500D75AE1 /* Elements */ = { 225 | isa = PBXGroup; 226 | children = ( 227 | A1D4CE2728C8555500D75AE1 /* PageControl.swift */, 228 | A1D4CE2328C712E600D75AE1 /* OnbardingCardView.swift */, 229 | A1D4CE2B28C86B5A00D75AE1 /* OnboardingContinueButton.swift */, 230 | ); 231 | path = Elements; 232 | sourceTree = ""; 233 | }; 234 | A1D4CE3028C86D7A00D75AE1 /* Model */ = { 235 | isa = PBXGroup; 236 | children = ( 237 | A1390E9928C07853007F5BA1 /* cards.json */, 238 | A1D4CE1F28C6501E00D75AE1 /* OnboardingCardsData.swift */, 239 | A1D4CE3228C89DD700D75AE1 /* OnboardingCard.swift */, 240 | ); 241 | path = Model; 242 | sourceTree = ""; 243 | }; 244 | /* End PBXGroup section */ 245 | 246 | /* Begin PBXNativeTarget section */ 247 | A1390E6928C073FE007F5BA1 /* OnboardingAnimationOne */ = { 248 | isa = PBXNativeTarget; 249 | buildConfigurationList = A1390E8E28C073FF007F5BA1 /* Build configuration list for PBXNativeTarget "OnboardingAnimationOne" */; 250 | buildPhases = ( 251 | A1390E6628C073FE007F5BA1 /* Sources */, 252 | A1390E6728C073FE007F5BA1 /* Frameworks */, 253 | A1390E6828C073FE007F5BA1 /* Resources */, 254 | ); 255 | buildRules = ( 256 | ); 257 | dependencies = ( 258 | ); 259 | name = OnboardingAnimationOne; 260 | productName = OnboardingAnimationOne; 261 | productReference = A1390E6A28C073FE007F5BA1 /* OnboardingAnimationOne.app */; 262 | productType = "com.apple.product-type.application"; 263 | }; 264 | A1390E7928C073FF007F5BA1 /* OnboardingAnimationOneTests */ = { 265 | isa = PBXNativeTarget; 266 | buildConfigurationList = A1390E9128C073FF007F5BA1 /* Build configuration list for PBXNativeTarget "OnboardingAnimationOneTests" */; 267 | buildPhases = ( 268 | A1390E7628C073FF007F5BA1 /* Sources */, 269 | A1390E7728C073FF007F5BA1 /* Frameworks */, 270 | A1390E7828C073FF007F5BA1 /* Resources */, 271 | ); 272 | buildRules = ( 273 | ); 274 | dependencies = ( 275 | A1390E7C28C073FF007F5BA1 /* PBXTargetDependency */, 276 | ); 277 | name = OnboardingAnimationOneTests; 278 | productName = OnboardingAnimationOneTests; 279 | productReference = A1390E7A28C073FF007F5BA1 /* OnboardingAnimationOneTests.xctest */; 280 | productType = "com.apple.product-type.bundle.unit-test"; 281 | }; 282 | A1390E8328C073FF007F5BA1 /* OnboardingAnimationOneUITests */ = { 283 | isa = PBXNativeTarget; 284 | buildConfigurationList = A1390E9428C073FF007F5BA1 /* Build configuration list for PBXNativeTarget "OnboardingAnimationOneUITests" */; 285 | buildPhases = ( 286 | A1390E8028C073FF007F5BA1 /* Sources */, 287 | A1390E8128C073FF007F5BA1 /* Frameworks */, 288 | A1390E8228C073FF007F5BA1 /* Resources */, 289 | ); 290 | buildRules = ( 291 | ); 292 | dependencies = ( 293 | A1390E8628C073FF007F5BA1 /* PBXTargetDependency */, 294 | ); 295 | name = OnboardingAnimationOneUITests; 296 | productName = OnboardingAnimationOneUITests; 297 | productReference = A1390E8428C073FF007F5BA1 /* OnboardingAnimationOneUITests.xctest */; 298 | productType = "com.apple.product-type.bundle.ui-testing"; 299 | }; 300 | /* End PBXNativeTarget section */ 301 | 302 | /* Begin PBXProject section */ 303 | A1390E6228C073FE007F5BA1 /* Project object */ = { 304 | isa = PBXProject; 305 | attributes = { 306 | BuildIndependentTargetsInParallel = 1; 307 | LastSwiftUpdateCheck = 1340; 308 | LastUpgradeCheck = 1340; 309 | TargetAttributes = { 310 | A1390E6928C073FE007F5BA1 = { 311 | CreatedOnToolsVersion = 13.4.1; 312 | }; 313 | A1390E7928C073FF007F5BA1 = { 314 | CreatedOnToolsVersion = 13.4.1; 315 | TestTargetID = A1390E6928C073FE007F5BA1; 316 | }; 317 | A1390E8328C073FF007F5BA1 = { 318 | CreatedOnToolsVersion = 13.4.1; 319 | TestTargetID = A1390E6928C073FE007F5BA1; 320 | }; 321 | }; 322 | }; 323 | buildConfigurationList = A1390E6528C073FE007F5BA1 /* Build configuration list for PBXProject "OnboardingAnimationOne" */; 324 | compatibilityVersion = "Xcode 13.0"; 325 | developmentRegion = en; 326 | hasScannedForEncodings = 0; 327 | knownRegions = ( 328 | en, 329 | Base, 330 | ); 331 | mainGroup = A1390E6128C073FE007F5BA1; 332 | productRefGroup = A1390E6B28C073FE007F5BA1 /* Products */; 333 | projectDirPath = ""; 334 | projectRoot = ""; 335 | targets = ( 336 | A1390E6928C073FE007F5BA1 /* OnboardingAnimationOne */, 337 | A1390E7928C073FF007F5BA1 /* OnboardingAnimationOneTests */, 338 | A1390E8328C073FF007F5BA1 /* OnboardingAnimationOneUITests */, 339 | ); 340 | }; 341 | /* End PBXProject section */ 342 | 343 | /* Begin PBXResourcesBuildPhase section */ 344 | A1390E6828C073FE007F5BA1 /* Resources */ = { 345 | isa = PBXResourcesBuildPhase; 346 | buildActionMask = 2147483647; 347 | files = ( 348 | A1390E7528C073FF007F5BA1 /* Preview Assets.xcassets in Resources */, 349 | A1390E9A28C07853007F5BA1 /* cards.json in Resources */, 350 | A1390E7228C073FF007F5BA1 /* Assets.xcassets in Resources */, 351 | ); 352 | runOnlyForDeploymentPostprocessing = 0; 353 | }; 354 | A1390E7828C073FF007F5BA1 /* Resources */ = { 355 | isa = PBXResourcesBuildPhase; 356 | buildActionMask = 2147483647; 357 | files = ( 358 | ); 359 | runOnlyForDeploymentPostprocessing = 0; 360 | }; 361 | A1390E8228C073FF007F5BA1 /* Resources */ = { 362 | isa = PBXResourcesBuildPhase; 363 | buildActionMask = 2147483647; 364 | files = ( 365 | ); 366 | runOnlyForDeploymentPostprocessing = 0; 367 | }; 368 | /* End PBXResourcesBuildPhase section */ 369 | 370 | /* Begin PBXSourcesBuildPhase section */ 371 | A1390E6628C073FE007F5BA1 /* Sources */ = { 372 | isa = PBXSourcesBuildPhase; 373 | buildActionMask = 2147483647; 374 | files = ( 375 | A1390E9828C07818007F5BA1 /* Bundle+Ext.swift in Sources */, 376 | A1D4CE2428C712E600D75AE1 /* OnbardingCardView.swift in Sources */, 377 | 4455A4782A6861CF00849DB8 /* View+Ext.swift in Sources */, 378 | 4455A46F2A673AF700849DB8 /* UnitRowsData.swift in Sources */, 379 | A1D4CE2828C8555500D75AE1 /* PageControl.swift in Sources */, 380 | A1390E7028C073FE007F5BA1 /* ContentView.swift in Sources */, 381 | A1D4CE2628C7384800D75AE1 /* AdaptivePagingScrollView.swift in Sources */, 382 | A1D4CE2C28C86B5A00D75AE1 /* OnboardingContinueButton.swift in Sources */, 383 | A1390E6E28C073FE007F5BA1 /* OnboardingAnimationOneApp.swift in Sources */, 384 | 4455A4742A673B9000849DB8 /* Orientation.swift in Sources */, 385 | 4455A4712A673B0000849DB8 /* UnitRow.swift in Sources */, 386 | A1D4CE2028C6501E00D75AE1 /* OnboardingCardsData.swift in Sources */, 387 | 448834AF2A67223F00B87509 /* UnitsView.swift in Sources */, 388 | 4455A4762A6861AD00849DB8 /* FrameModifier.swift in Sources */, 389 | A1D4CE3328C89DD700D75AE1 /* OnboardingCard.swift in Sources */, 390 | 4455A46B2A673A9C00849DB8 /* UnitView.swift in Sources */, 391 | A1D4CE2A28C86B4100D75AE1 /* Color+Ext.swift in Sources */, 392 | A1D4CE3528C89E0C00D75AE1 /* OnboardingView.swift in Sources */, 393 | ); 394 | runOnlyForDeploymentPostprocessing = 0; 395 | }; 396 | A1390E7628C073FF007F5BA1 /* Sources */ = { 397 | isa = PBXSourcesBuildPhase; 398 | buildActionMask = 2147483647; 399 | files = ( 400 | ); 401 | runOnlyForDeploymentPostprocessing = 0; 402 | }; 403 | A1390E8028C073FF007F5BA1 /* Sources */ = { 404 | isa = PBXSourcesBuildPhase; 405 | buildActionMask = 2147483647; 406 | files = ( 407 | ); 408 | runOnlyForDeploymentPostprocessing = 0; 409 | }; 410 | /* End PBXSourcesBuildPhase section */ 411 | 412 | /* Begin PBXTargetDependency section */ 413 | A1390E7C28C073FF007F5BA1 /* PBXTargetDependency */ = { 414 | isa = PBXTargetDependency; 415 | target = A1390E6928C073FE007F5BA1 /* OnboardingAnimationOne */; 416 | targetProxy = A1390E7B28C073FF007F5BA1 /* PBXContainerItemProxy */; 417 | }; 418 | A1390E8628C073FF007F5BA1 /* PBXTargetDependency */ = { 419 | isa = PBXTargetDependency; 420 | target = A1390E6928C073FE007F5BA1 /* OnboardingAnimationOne */; 421 | targetProxy = A1390E8528C073FF007F5BA1 /* PBXContainerItemProxy */; 422 | }; 423 | /* End PBXTargetDependency section */ 424 | 425 | /* Begin XCBuildConfiguration section */ 426 | A1390E8C28C073FF007F5BA1 /* Debug */ = { 427 | isa = XCBuildConfiguration; 428 | buildSettings = { 429 | ALWAYS_SEARCH_USER_PATHS = NO; 430 | CLANG_ANALYZER_NONNULL = YES; 431 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 432 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 433 | CLANG_ENABLE_MODULES = YES; 434 | CLANG_ENABLE_OBJC_ARC = YES; 435 | CLANG_ENABLE_OBJC_WEAK = YES; 436 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 437 | CLANG_WARN_BOOL_CONVERSION = YES; 438 | CLANG_WARN_COMMA = YES; 439 | CLANG_WARN_CONSTANT_CONVERSION = YES; 440 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 441 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 442 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 443 | CLANG_WARN_EMPTY_BODY = YES; 444 | CLANG_WARN_ENUM_CONVERSION = YES; 445 | CLANG_WARN_INFINITE_RECURSION = YES; 446 | CLANG_WARN_INT_CONVERSION = YES; 447 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 448 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 449 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 450 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 451 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 452 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 453 | CLANG_WARN_STRICT_PROTOTYPES = YES; 454 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 455 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 456 | CLANG_WARN_UNREACHABLE_CODE = YES; 457 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 458 | COPY_PHASE_STRIP = NO; 459 | DEBUG_INFORMATION_FORMAT = dwarf; 460 | ENABLE_STRICT_OBJC_MSGSEND = YES; 461 | ENABLE_TESTABILITY = YES; 462 | GCC_C_LANGUAGE_STANDARD = gnu11; 463 | GCC_DYNAMIC_NO_PIC = NO; 464 | GCC_NO_COMMON_BLOCKS = YES; 465 | GCC_OPTIMIZATION_LEVEL = 0; 466 | GCC_PREPROCESSOR_DEFINITIONS = ( 467 | "DEBUG=1", 468 | "$(inherited)", 469 | ); 470 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 471 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 472 | GCC_WARN_UNDECLARED_SELECTOR = YES; 473 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 474 | GCC_WARN_UNUSED_FUNCTION = YES; 475 | GCC_WARN_UNUSED_VARIABLE = YES; 476 | IPHONEOS_DEPLOYMENT_TARGET = 15.5; 477 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 478 | MTL_FAST_MATH = YES; 479 | ONLY_ACTIVE_ARCH = YES; 480 | SDKROOT = iphoneos; 481 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 482 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 483 | }; 484 | name = Debug; 485 | }; 486 | A1390E8D28C073FF007F5BA1 /* Release */ = { 487 | isa = XCBuildConfiguration; 488 | buildSettings = { 489 | ALWAYS_SEARCH_USER_PATHS = NO; 490 | CLANG_ANALYZER_NONNULL = YES; 491 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 492 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 493 | CLANG_ENABLE_MODULES = YES; 494 | CLANG_ENABLE_OBJC_ARC = YES; 495 | CLANG_ENABLE_OBJC_WEAK = YES; 496 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 497 | CLANG_WARN_BOOL_CONVERSION = YES; 498 | CLANG_WARN_COMMA = YES; 499 | CLANG_WARN_CONSTANT_CONVERSION = YES; 500 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 501 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 502 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 503 | CLANG_WARN_EMPTY_BODY = YES; 504 | CLANG_WARN_ENUM_CONVERSION = YES; 505 | CLANG_WARN_INFINITE_RECURSION = YES; 506 | CLANG_WARN_INT_CONVERSION = YES; 507 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 508 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 509 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 510 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 511 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 512 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 513 | CLANG_WARN_STRICT_PROTOTYPES = YES; 514 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 515 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 516 | CLANG_WARN_UNREACHABLE_CODE = YES; 517 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 518 | COPY_PHASE_STRIP = NO; 519 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 520 | ENABLE_NS_ASSERTIONS = NO; 521 | ENABLE_STRICT_OBJC_MSGSEND = YES; 522 | GCC_C_LANGUAGE_STANDARD = gnu11; 523 | GCC_NO_COMMON_BLOCKS = YES; 524 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 525 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 526 | GCC_WARN_UNDECLARED_SELECTOR = YES; 527 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 528 | GCC_WARN_UNUSED_FUNCTION = YES; 529 | GCC_WARN_UNUSED_VARIABLE = YES; 530 | IPHONEOS_DEPLOYMENT_TARGET = 15.5; 531 | MTL_ENABLE_DEBUG_INFO = NO; 532 | MTL_FAST_MATH = YES; 533 | SDKROOT = iphoneos; 534 | SWIFT_COMPILATION_MODE = wholemodule; 535 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 536 | VALIDATE_PRODUCT = YES; 537 | }; 538 | name = Release; 539 | }; 540 | A1390E8F28C073FF007F5BA1 /* Debug */ = { 541 | isa = XCBuildConfiguration; 542 | buildSettings = { 543 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 544 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 545 | CODE_SIGN_STYLE = Automatic; 546 | CURRENT_PROJECT_VERSION = 1; 547 | DEVELOPMENT_ASSET_PATHS = "\"OnboardingAnimationOne/Preview Content\""; 548 | DEVELOPMENT_TEAM = S4RZY48S82; 549 | ENABLE_PREVIEWS = YES; 550 | GENERATE_INFOPLIST_FILE = YES; 551 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 552 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 553 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 554 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 555 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 556 | LD_RUNPATH_SEARCH_PATHS = ( 557 | "$(inherited)", 558 | "@executable_path/Frameworks", 559 | ); 560 | MARKETING_VERSION = 1.0; 561 | PRODUCT_BUNDLE_IDENTIFIER = com.borisdipner.OnboardingAnimationOne; 562 | PRODUCT_NAME = "$(TARGET_NAME)"; 563 | SWIFT_EMIT_LOC_STRINGS = YES; 564 | SWIFT_VERSION = 5.0; 565 | TARGETED_DEVICE_FAMILY = "1,2"; 566 | }; 567 | name = Debug; 568 | }; 569 | A1390E9028C073FF007F5BA1 /* Release */ = { 570 | isa = XCBuildConfiguration; 571 | buildSettings = { 572 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 573 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 574 | CODE_SIGN_STYLE = Automatic; 575 | CURRENT_PROJECT_VERSION = 1; 576 | DEVELOPMENT_ASSET_PATHS = "\"OnboardingAnimationOne/Preview Content\""; 577 | DEVELOPMENT_TEAM = S4RZY48S82; 578 | ENABLE_PREVIEWS = YES; 579 | GENERATE_INFOPLIST_FILE = YES; 580 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 581 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 582 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 583 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 584 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 585 | LD_RUNPATH_SEARCH_PATHS = ( 586 | "$(inherited)", 587 | "@executable_path/Frameworks", 588 | ); 589 | MARKETING_VERSION = 1.0; 590 | PRODUCT_BUNDLE_IDENTIFIER = com.borisdipner.OnboardingAnimationOne; 591 | PRODUCT_NAME = "$(TARGET_NAME)"; 592 | SWIFT_EMIT_LOC_STRINGS = YES; 593 | SWIFT_VERSION = 5.0; 594 | TARGETED_DEVICE_FAMILY = "1,2"; 595 | }; 596 | name = Release; 597 | }; 598 | A1390E9228C073FF007F5BA1 /* Debug */ = { 599 | isa = XCBuildConfiguration; 600 | buildSettings = { 601 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 602 | BUNDLE_LOADER = "$(TEST_HOST)"; 603 | CODE_SIGN_STYLE = Automatic; 604 | CURRENT_PROJECT_VERSION = 1; 605 | DEVELOPMENT_TEAM = S4RZY48S82; 606 | GENERATE_INFOPLIST_FILE = YES; 607 | IPHONEOS_DEPLOYMENT_TARGET = 15.5; 608 | MARKETING_VERSION = 1.0; 609 | PRODUCT_BUNDLE_IDENTIFIER = com.borisdipner.OnboardingAnimationOneTests; 610 | PRODUCT_NAME = "$(TARGET_NAME)"; 611 | SWIFT_EMIT_LOC_STRINGS = NO; 612 | SWIFT_VERSION = 5.0; 613 | TARGETED_DEVICE_FAMILY = "1,2"; 614 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/OnboardingAnimationOne.app/OnboardingAnimationOne"; 615 | }; 616 | name = Debug; 617 | }; 618 | A1390E9328C073FF007F5BA1 /* Release */ = { 619 | isa = XCBuildConfiguration; 620 | buildSettings = { 621 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 622 | BUNDLE_LOADER = "$(TEST_HOST)"; 623 | CODE_SIGN_STYLE = Automatic; 624 | CURRENT_PROJECT_VERSION = 1; 625 | DEVELOPMENT_TEAM = S4RZY48S82; 626 | GENERATE_INFOPLIST_FILE = YES; 627 | IPHONEOS_DEPLOYMENT_TARGET = 15.5; 628 | MARKETING_VERSION = 1.0; 629 | PRODUCT_BUNDLE_IDENTIFIER = com.borisdipner.OnboardingAnimationOneTests; 630 | PRODUCT_NAME = "$(TARGET_NAME)"; 631 | SWIFT_EMIT_LOC_STRINGS = NO; 632 | SWIFT_VERSION = 5.0; 633 | TARGETED_DEVICE_FAMILY = "1,2"; 634 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/OnboardingAnimationOne.app/OnboardingAnimationOne"; 635 | }; 636 | name = Release; 637 | }; 638 | A1390E9528C073FF007F5BA1 /* Debug */ = { 639 | isa = XCBuildConfiguration; 640 | buildSettings = { 641 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 642 | CODE_SIGN_STYLE = Automatic; 643 | CURRENT_PROJECT_VERSION = 1; 644 | DEVELOPMENT_TEAM = S4RZY48S82; 645 | GENERATE_INFOPLIST_FILE = YES; 646 | MARKETING_VERSION = 1.0; 647 | PRODUCT_BUNDLE_IDENTIFIER = com.borisdipner.OnboardingAnimationOneUITests; 648 | PRODUCT_NAME = "$(TARGET_NAME)"; 649 | SWIFT_EMIT_LOC_STRINGS = NO; 650 | SWIFT_VERSION = 5.0; 651 | TARGETED_DEVICE_FAMILY = "1,2"; 652 | TEST_TARGET_NAME = OnboardingAnimationOne; 653 | }; 654 | name = Debug; 655 | }; 656 | A1390E9628C073FF007F5BA1 /* Release */ = { 657 | isa = XCBuildConfiguration; 658 | buildSettings = { 659 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 660 | CODE_SIGN_STYLE = Automatic; 661 | CURRENT_PROJECT_VERSION = 1; 662 | DEVELOPMENT_TEAM = S4RZY48S82; 663 | GENERATE_INFOPLIST_FILE = YES; 664 | MARKETING_VERSION = 1.0; 665 | PRODUCT_BUNDLE_IDENTIFIER = com.borisdipner.OnboardingAnimationOneUITests; 666 | PRODUCT_NAME = "$(TARGET_NAME)"; 667 | SWIFT_EMIT_LOC_STRINGS = NO; 668 | SWIFT_VERSION = 5.0; 669 | TARGETED_DEVICE_FAMILY = "1,2"; 670 | TEST_TARGET_NAME = OnboardingAnimationOne; 671 | }; 672 | name = Release; 673 | }; 674 | /* End XCBuildConfiguration section */ 675 | 676 | /* Begin XCConfigurationList section */ 677 | A1390E6528C073FE007F5BA1 /* Build configuration list for PBXProject "OnboardingAnimationOne" */ = { 678 | isa = XCConfigurationList; 679 | buildConfigurations = ( 680 | A1390E8C28C073FF007F5BA1 /* Debug */, 681 | A1390E8D28C073FF007F5BA1 /* Release */, 682 | ); 683 | defaultConfigurationIsVisible = 0; 684 | defaultConfigurationName = Release; 685 | }; 686 | A1390E8E28C073FF007F5BA1 /* Build configuration list for PBXNativeTarget "OnboardingAnimationOne" */ = { 687 | isa = XCConfigurationList; 688 | buildConfigurations = ( 689 | A1390E8F28C073FF007F5BA1 /* Debug */, 690 | A1390E9028C073FF007F5BA1 /* Release */, 691 | ); 692 | defaultConfigurationIsVisible = 0; 693 | defaultConfigurationName = Release; 694 | }; 695 | A1390E9128C073FF007F5BA1 /* Build configuration list for PBXNativeTarget "OnboardingAnimationOneTests" */ = { 696 | isa = XCConfigurationList; 697 | buildConfigurations = ( 698 | A1390E9228C073FF007F5BA1 /* Debug */, 699 | A1390E9328C073FF007F5BA1 /* Release */, 700 | ); 701 | defaultConfigurationIsVisible = 0; 702 | defaultConfigurationName = Release; 703 | }; 704 | A1390E9428C073FF007F5BA1 /* Build configuration list for PBXNativeTarget "OnboardingAnimationOneUITests" */ = { 705 | isa = XCConfigurationList; 706 | buildConfigurations = ( 707 | A1390E9528C073FF007F5BA1 /* Debug */, 708 | A1390E9628C073FF007F5BA1 /* Release */, 709 | ); 710 | defaultConfigurationIsVisible = 0; 711 | defaultConfigurationName = Release; 712 | }; 713 | /* End XCConfigurationList section */ 714 | }; 715 | rootObject = A1390E6228C073FE007F5BA1 /* Project object */; 716 | } 717 | -------------------------------------------------------------------------------- /OnboardingAnimationOne.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /OnboardingAnimationOne.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /OnboardingAnimationOne.xcodeproj/project.xcworkspace/xcuserdata/boris.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borisdipner/paging-srollview-swiftui/410b062050b295d79c01015bcfc172f3c645d7b2/OnboardingAnimationOne.xcodeproj/project.xcworkspace/xcuserdata/boris.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /OnboardingAnimationOne.xcodeproj/xcuserdata/boris.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 21 | 22 | 23 | 25 | 37 | 38 | 39 | 41 | 53 | 54 | 55 | 57 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /OnboardingAnimationOne.xcodeproj/xcuserdata/boris.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | OnboardingAnimationOne.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borisdipner/paging-srollview-swiftui/410b062050b295d79c01015bcfc172f3c645d7b2/OnboardingAnimationOne/.DS_Store -------------------------------------------------------------------------------- /OnboardingAnimationOne/App/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borisdipner/paging-srollview-swiftui/410b062050b295d79c01015bcfc172f3c645d7b2/OnboardingAnimationOne/App/.DS_Store -------------------------------------------------------------------------------- /OnboardingAnimationOne/App/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // OnboardingAnimationOne 4 | // 5 | // Created by Boris on 01.09.2022. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ContentView: View { 11 | 12 | @State private var isShowingUnitsSheet = false 13 | @State private var isShowingOnboardingSheet = false 14 | 15 | var body: some View { 16 | VStack(alignment: .leading) { 17 | Text("Paging Previews") 18 | .font(.system(size: 36, weight: .semibold)) 19 | .padding(16) 20 | List { 21 | Button("Vertical", action: { isShowingUnitsSheet.toggle() }) 22 | .fullScreenCover(isPresented: $isShowingUnitsSheet) { 23 | UnitsView(value: .constant("1"), isPresented: $isShowingUnitsSheet) 24 | } 25 | Button("Horizontal", action: { isShowingOnboardingSheet.toggle() }) 26 | .fullScreenCover(isPresented: $isShowingOnboardingSheet) { 27 | OnboardingView(isPresented: $isShowingOnboardingSheet) 28 | } 29 | } 30 | .foregroundColor(.black) 31 | } 32 | } 33 | } 34 | 35 | struct ContentView_Previews: PreviewProvider { 36 | static var previews: some View { 37 | ContentView() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/App/OnboardingAnimationOneApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OnboardingAnimationOneApp.swift 3 | // OnboardingAnimationOne 4 | // 5 | // Created by Boris on 01.09.2022. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct OnboardingAnimationOneApp: App { 12 | var body: some Scene { 13 | WindowGroup { 14 | ContentView() 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/App/Views/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borisdipner/paging-srollview-swiftui/410b062050b295d79c01015bcfc172f3c645d7b2/OnboardingAnimationOne/App/Views/.DS_Store -------------------------------------------------------------------------------- /OnboardingAnimationOne/App/Views/Common/AdaptivePagingScrollView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AdaptivePagingScrollView.swift 3 | // OnboardingAnimationOne 4 | // 5 | // Created by Boris on 06.09.2022. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct AdaptivePagingScrollView: View { 11 | 12 | private let items: [AnyView] 13 | private let itemPadding: CGFloat 14 | private let itemSpacing: CGFloat 15 | private let itemScrollableSide: CGFloat 16 | private let itemsAmount: Int 17 | private let visibleContentLength: CGFloat 18 | 19 | private let initialOffset: CGFloat 20 | private let scrollDampingFactor: CGFloat = 0.66 21 | private let orientation: Orientation 22 | 23 | @Binding var currentPageIndex: Int 24 | 25 | @State private var currentScrollOffset: CGFloat = 0 26 | @State private var gestureDragOffset: CGFloat = 0 27 | 28 | private func countOffset(for pageIndex: Int) -> CGFloat { 29 | 30 | let activePageOffset = CGFloat(pageIndex) * (itemScrollableSide + itemPadding) 31 | return initialOffset - activePageOffset 32 | } 33 | 34 | private func countPageIndex(for offset: CGFloat) -> Int { 35 | 36 | guard itemsAmount > 0 else { return 0 } 37 | 38 | let offset = countLogicalOffset(offset) 39 | let floatIndex = (offset)/(itemScrollableSide + itemPadding) 40 | 41 | var index = Int(round(floatIndex)) 42 | if max(index, 0) > itemsAmount { 43 | index = itemsAmount 44 | } 45 | 46 | return min(max(index, 0), itemsAmount - 1) 47 | } 48 | 49 | private func countCurrentScrollOffset() -> CGFloat { 50 | return countOffset(for: currentPageIndex) + gestureDragOffset 51 | } 52 | 53 | private func countLogicalOffset(_ trueOffset: CGFloat) -> CGFloat { 54 | return (trueOffset-initialOffset) * -1.0 55 | } 56 | 57 | private func changeFocus() { 58 | withAnimation { 59 | currentScrollOffset = countOffset(for: currentPageIndex) 60 | } 61 | } 62 | 63 | init(currentPageIndex: Binding, 64 | itemsAmount: Int, 65 | itemScrollableSide: CGFloat, 66 | itemPadding: CGFloat, 67 | visibleContentLength: CGFloat, 68 | orientation: Orientation, 69 | @ViewBuilder content: () -> A) { 70 | 71 | let views = content() 72 | self.items = [AnyView(views)] 73 | 74 | self._currentPageIndex = currentPageIndex 75 | 76 | self.itemsAmount = itemsAmount 77 | self.itemSpacing = itemPadding 78 | self.itemScrollableSide = itemScrollableSide 79 | self.itemPadding = itemPadding 80 | self.visibleContentLength = visibleContentLength 81 | self.orientation = orientation 82 | 83 | let itemRemain = (visibleContentLength-itemScrollableSide-2*itemPadding)/2 84 | self.initialOffset = itemRemain + itemPadding 85 | } 86 | 87 | @ViewBuilder 88 | func contentView() -> some View { 89 | switch orientation { 90 | case .horizontal: 91 | HStack(alignment: .center, spacing: itemSpacing) { 92 | ForEach(items.indices, id: \.self) { itemIndex in 93 | items[itemIndex].frame(width: itemScrollableSide) 94 | } 95 | } 96 | case .vertical: 97 | VStack(alignment: .leading, spacing: itemSpacing) { 98 | ForEach(items.indices, id: \.self) { itemIndex in 99 | items[itemIndex].frame(height: itemScrollableSide) 100 | } 101 | } 102 | } 103 | } 104 | 105 | var body: some View { 106 | GeometryReader { _ in 107 | contentView() 108 | } 109 | .onAppear { 110 | currentScrollOffset = countOffset(for: currentPageIndex) 111 | } 112 | .background(Color.black.opacity(0.00001)) // hack - this allows gesture recognizing even when background is transparent 113 | .frameModifier(visibleContentLength, currentScrollOffset, orientation) 114 | .simultaneousGesture( 115 | DragGesture(minimumDistance: 1, coordinateSpace: .local) 116 | .onChanged { value in 117 | switch orientation { 118 | case .horizontal: 119 | gestureDragOffset = value.translation.width 120 | case .vertical: 121 | gestureDragOffset = value.translation.height 122 | } 123 | currentScrollOffset = countCurrentScrollOffset() 124 | } 125 | .onEnded { value in 126 | let cleanOffset: CGFloat 127 | switch orientation { 128 | case .horizontal: 129 | cleanOffset = (value.predictedEndTranslation.width - gestureDragOffset) 130 | case .vertical: 131 | cleanOffset = (value.predictedEndTranslation.height - gestureDragOffset) 132 | } 133 | let velocityDiff = cleanOffset * scrollDampingFactor 134 | 135 | var newPageIndex = countPageIndex(for: currentScrollOffset + velocityDiff) 136 | 137 | let currentItemOffset = CGFloat(currentPageIndex) * (itemScrollableSide + itemPadding) 138 | 139 | if currentScrollOffset < -(currentItemOffset), 140 | newPageIndex == currentPageIndex { 141 | newPageIndex += 1 142 | } 143 | 144 | gestureDragOffset = 0 145 | 146 | withAnimation(.interpolatingSpring(mass: 0.1, 147 | stiffness: 20, 148 | damping: 1.5, 149 | initialVelocity: 0)) { 150 | self.currentPageIndex = newPageIndex 151 | self.currentScrollOffset = self.countCurrentScrollOffset() 152 | } 153 | } 154 | ) 155 | .contentShape(Rectangle()) 156 | .onChange(of: currentPageIndex, perform: { _ in changeFocus() }) 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/App/Views/Common/FrameModifier.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FrameModifier.swift 3 | // OnboardingAnimationOne 4 | // 5 | // Created by Mariya Pankova on 19.07.2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct FrameModifier: ViewModifier { 11 | let contentLength: CGFloat 12 | let currentScrollOffset: CGFloat 13 | let orientation: Orientation 14 | 15 | init (contentLength: CGFloat, 16 | visibleContentLength: CGFloat, 17 | currentScrollOffset: CGFloat, 18 | orientation: Orientation) { 19 | self.contentLength = contentLength 20 | self.currentScrollOffset = currentScrollOffset 21 | self.orientation = orientation 22 | } 23 | 24 | func body(content: Content) -> some View { 25 | switch orientation { 26 | case .horizontal: 27 | return content 28 | .frame(width: contentLength) 29 | .offset(x: self.currentScrollOffset, y: 0) 30 | case .vertical: 31 | return content 32 | .frame(height: contentLength) 33 | .offset(x: 0, y: self.currentScrollOffset) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/App/Views/Common/Orientation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Orientation.swift 3 | // OnboardingAnimationOne 4 | // 5 | // Created by Mariya Pankova on 18.07.2023. 6 | // 7 | 8 | enum Orientation { 9 | case horizontal, vertical 10 | } 11 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/App/Views/Onboarding/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borisdipner/paging-srollview-swiftui/410b062050b295d79c01015bcfc172f3c645d7b2/OnboardingAnimationOne/App/Views/Onboarding/.DS_Store -------------------------------------------------------------------------------- /OnboardingAnimationOne/App/Views/Onboarding/Elements/OnbardingCardView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OnbardingCardView.swift 3 | // OnboardingAnimationOne 4 | // 5 | // Created by Boris on 06.09.2022. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct OnbardingCardView: View { 11 | 12 | var card: OnboardingCard 13 | 14 | var body: some View { 15 | VStack(alignment: .leading, spacing: 25) { 16 | Text(card.title.capitalized) 17 | .colorInvert() 18 | .font(.system(size: 48, weight: .bold, design: .rounded)) 19 | VStack(alignment: .leading, spacing: 25) { 20 | Text(card.subTitle) 21 | .colorInvert() 22 | .font(.bold(.headline)()) 23 | .lineLimit(3) 24 | .frame(width: 200) 25 | .fixedSize() 26 | .padding() 27 | Image(card.image) 28 | .resizable() 29 | .scaledToFit() 30 | .frame(width: 220, height: 200, alignment: .center) 31 | } 32 | .padding(20) 33 | .background(Color.nightBlue) 34 | .cornerRadius(25) 35 | } 36 | } 37 | } 38 | 39 | 40 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/App/Views/Onboarding/Elements/OnboardingContinueButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OnboardingContinueButton.swift 3 | // OnboardingAnimationOne 4 | // 5 | // Created by Boris on 07.09.2022. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct OnboardingContinueButton: View { 11 | 12 | @Binding var isReadyToContinue: Bool 13 | @Binding var isPresented: Bool 14 | @State private var animateGradient = false 15 | 16 | var body: some View { 17 | Button(action: { isPresented = false }) { 18 | 19 | let buttonTitle = isReadyToContinue ? "Let's continue" : "Skip" 20 | let buttonImage = isReadyToContinue ? "location.fill" : "location" 21 | let gradientColors = isReadyToContinue ? [Color.purple, Color.nightBlue] : [Color.purple, Color.black] 22 | 23 | HStack { 24 | Image(systemName: buttonImage) 25 | .foregroundColor(Color.white) 26 | .frame(width: 20, height: 20) 27 | .padding(EdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 0)) 28 | Text(buttonTitle) 29 | .font(.bold(.subheadline)()) 30 | .foregroundColor(Color.white) 31 | .padding(EdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 40)) 32 | 33 | } 34 | .padding(5) 35 | .background( 36 | LinearGradient(colors: gradientColors, 37 | startPoint: animateGradient ? .topLeading : .bottomLeading, 38 | endPoint: animateGradient ? .bottomTrailing : .topTrailing) 39 | .ignoresSafeArea() 40 | ) 41 | .cornerRadius(15) 42 | } 43 | .onAppear { 44 | withAnimation(.easeOut(duration: 2.0).repeatForever(autoreverses: true)) { 45 | animateGradient.toggle() 46 | } 47 | } 48 | } 49 | } 50 | 51 | 52 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/App/Views/Onboarding/Elements/PageControl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PageControl.swift 3 | // OnboardingAnimationOne 4 | // 5 | // Created by Boris on 07.09.2022. 6 | // 7 | 8 | import SwiftUI 9 | import UIKit 10 | 11 | struct PageControl: UIViewRepresentable { 12 | 13 | var numberOfPages: Int 14 | @Binding var currentPage: Int 15 | 16 | func makeCoordinator() -> Coordinator { 17 | Coordinator(self) 18 | } 19 | 20 | func makeUIView(context: Context) -> UIPageControl { 21 | 22 | let control = UIPageControl() 23 | control.numberOfPages = numberOfPages 24 | control.addTarget(context.coordinator, 25 | action: #selector(Coordinator.updateCurrentPage(sender:)), 26 | for: .valueChanged) 27 | return control 28 | } 29 | 30 | func updateUIView(_ uiView: UIPageControl, context: Context) { 31 | uiView.currentPage = currentPage 32 | } 33 | 34 | class Coordinator: NSObject { 35 | 36 | var control: PageControl 37 | 38 | init(_ control: PageControl) { 39 | self.control = control 40 | } 41 | 42 | @objc 43 | func updateCurrentPage(sender: UIPageControl) { 44 | control.currentPage = sender.currentPage 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/App/Views/Onboarding/Model/OnboardingCard.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OnboardingCard.swift 3 | // OnboardingAnimationOne 4 | // 5 | // Created by Boris on 07.09.2022. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct OnboardingCard: Codable, Identifiable, Equatable { 11 | 12 | var id: UUID 13 | var title: String 14 | var subTitle: String 15 | var image: String 16 | } 17 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/App/Views/Onboarding/Model/OnboardingCardsData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PageViewController.swift 3 | // OnboardingAnimationOne 4 | // 5 | // Created by Boris on 05.09.2022. 6 | // 7 | 8 | import SwiftUI 9 | 10 | class OnboardingCardsData: ObservableObject { 11 | 12 | let cards: [OnboardingCard] 13 | 14 | var primary: OnboardingCard { 15 | cards.first! 16 | } 17 | 18 | init() { 19 | cards = Bundle.main.decode([OnboardingCard].self, from: "cards.json") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/App/Views/Onboarding/Model/cards.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id" : "ab8932ca-29bf-11ed-a261-0242ac120002", 4 | "title": "Read\nbooks,\neasy.", 5 | "subTitle": "No matter where you go,\nyour books always with you.", 6 | "image": "card_image_01" 7 | }, 8 | { 9 | "id" : "ab8932ca-29bf-11ed-a261-0242ac120001", 10 | "title": "Share\nwith\nfriends.", 11 | "subTitle": "Your reading lib migth be shared with community and friends.", 12 | "image": "card_image_02" 13 | }, 14 | { 15 | "id" : "ab8932ca-29bf-11ed-a261-0242ac120004", 16 | "title": "Track\nyour\nprogress.", 17 | "subTitle": "Get insights into your reading preferencies and anlytics of time spent", 18 | "image": "card_image_03" 19 | } 20 | ] 21 | 22 | 23 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/App/Views/Onboarding/OnboardingView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OnboardingView.swift 3 | // OnboardingAnimationOne 4 | // 5 | // Created by Boris on 07.09.2022. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct OnboardingView: View { 11 | 12 | let onboardData = OnboardingCardsData() 13 | 14 | @State private var scrollEffectValue: Double = 13 15 | @State private var activePageIndex: Int = 0 16 | @Binding var isPresented: Bool 17 | 18 | let itemWidth: CGFloat = 260 19 | let itemPadding: CGFloat = 20 20 | 21 | var body: some View { 22 | ZStack{ 23 | VStack(alignment: .center, spacing: 25) { 24 | HStack { 25 | PageControl(numberOfPages: self.onboardData.cards.count, 26 | currentPage: $activePageIndex) 27 | .foregroundColor(Color.nightBlue) 28 | .aspectRatio(3 / 2, contentMode: .fit) 29 | .frame(width: 165, height: 42) 30 | Spacer() 31 | } 32 | GeometryReader { geometry in 33 | AdaptivePagingScrollView(currentPageIndex: self.$activePageIndex, 34 | itemsAmount: self.onboardData.cards.count - 1, 35 | itemScrollableSide: self.itemWidth, 36 | itemPadding: self.itemPadding, 37 | visibleContentLength: geometry.size.width, 38 | orientation: .horizontal) { 39 | ForEach(onboardData.cards) { card in 40 | GeometryReader { screen in 41 | OnbardingCardView(card: card) 42 | .rotation3DEffect(Angle(degrees: (Double(screen.frame(in: .global).minX) - 20) / -15), 43 | axis: (x: 0, y: 90.0, z: 0)) 44 | 45 | .scaleEffect(activePageIndex == onboardData.cards.firstIndex(of: card) ?? 0 ? 1.05 : 1) 46 | } 47 | .frame(width: self.itemWidth, height: 600) 48 | } 49 | } 50 | } 51 | 52 | Spacer() 53 | 54 | OnboardingContinueButton( 55 | isReadyToContinue: .constant(onboardData.cards.count - 1 == activePageIndex), 56 | isPresented: $isPresented 57 | ) 58 | } 59 | } 60 | .background(Color.black) 61 | } 62 | } 63 | 64 | struct OnboardingView_Previews: PreviewProvider { 65 | static var previews: some View { 66 | OnboardingView(isPresented: .constant(true)) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/App/Views/Units/Elements/UnitView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UnitView.swift 3 | // OnboardingAnimationOne 4 | // 5 | // Created by Mariya Pankova on 18.07.2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct UnitView: View { 11 | let itemSide: CGFloat 12 | var value: String 13 | var unit: String 14 | var unitName: String 15 | 16 | var body: some View { 17 | VStack(alignment: .trailing) { 18 | HStack { 19 | Text(value) 20 | .font(.system(size: 20)) 21 | Text(unit) 22 | .font(.system(size: 16)) 23 | Spacer() 24 | } 25 | Text(unitName) 26 | .font(.system(size: 14)) 27 | } 28 | .minimumScaleFactor(0.1) 29 | .padding(12) 30 | .frame(height: itemSide) 31 | .background(.white) 32 | .cornerRadius(16) 33 | } 34 | } 35 | 36 | struct UnitView_Previews: PreviewProvider { 37 | static var previews: some View { 38 | UnitView(itemSide: 80, value: "5", unit: "Kilogram", unitName: "kg") 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/App/Views/Units/Model/UnitRow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UnitRow.swift 3 | // OnboardingAnimationOne 4 | // 5 | // Created by Mariya Pankova on 18.07.2023. 6 | // 7 | 8 | import Foundation 9 | 10 | struct UnitRow: Codable, Equatable, Hashable { 11 | 12 | var unit: String 13 | var unitName: String 14 | } 15 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/App/Views/Units/Model/UnitRowsData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UnitRowsData.swift 3 | // OnboardingAnimationOne 4 | // 5 | // Created by Mariya Pankova on 18.07.2023. 6 | // 7 | 8 | import Foundation 9 | 10 | class UnitRowsData: ObservableObject { 11 | 12 | let units: [UnitRow] 13 | 14 | var primary: UnitRow { 15 | units.first! 16 | } 17 | 18 | init() { 19 | units = [ 20 | .init(unit: "kg", unitName: "Kilogram"), 21 | .init(unit: "g", unitName: "Gram"), 22 | .init(unit: "mg", unitName: "Miligram"), 23 | .init(unit: "lb", unitName: "Pound"), 24 | ] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/App/Views/Units/UnitsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UnitsView.swift 3 | // OnboardingAnimationOne 4 | // 5 | // Created by Mariya Pankova on 18.07.2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct UnitsView: View { 11 | 12 | @State var activeIndex: Int = 0 13 | @Binding var value: String 14 | @Binding var isPresented: Bool 15 | 16 | let unitData: UnitRowsData 17 | let itemSide: CGFloat 18 | let itemPadding: CGFloat 19 | let visibleContentLength: CGFloat 20 | let onChangeActiveIndex: (Int) -> () 21 | 22 | var body: some View { 23 | VStack { 24 | Spacer(minLength: 160) 25 | GeometryReader { geometry in 26 | AdaptivePagingScrollView(currentPageIndex: $activeIndex, 27 | itemsAmount: unitData.units.count - 1, 28 | itemScrollableSide: itemSide, 29 | itemPadding: itemPadding, 30 | visibleContentLength: visibleContentLength, 31 | orientation: .vertical) { 32 | ForEach(unitData.units, id: \.self) { card in 33 | let isSelectedCard = activeIndex == unitData.units.firstIndex(of: card) ?? 0 34 | GeometryReader { screen in 35 | UnitView( 36 | itemSide: itemSide, 37 | value: isSelectedCard ? value : "", 38 | unit: card.unit, 39 | unitName: card.unitName 40 | ) 41 | .scaleEffect(isSelectedCard ? 1 : 0.9) 42 | } 43 | 44 | } 45 | } 46 | .clipped() 47 | .background(Color.black.opacity(0.2)) 48 | } 49 | Spacer() 50 | Button("Dismiss", action: { isPresented = false }) 51 | .padding(8) 52 | .background(Color.black.opacity(0.3)) 53 | .font(Font.system(size: 20, weight: .semibold)) 54 | .foregroundColor(Color.white) 55 | .cornerRadius(8) 56 | Spacer() 57 | } 58 | .onChange(of: activeIndex, perform: onChangeActiveIndex) 59 | } 60 | 61 | init(unitData: UnitRowsData = .init(), 62 | value: Binding = .constant(""), 63 | isPresented: Binding, 64 | itemSide: CGFloat = 80, 65 | itemPadding: CGFloat = 16, 66 | visibleContentLength: CGFloat = 300, 67 | onChangeActiveIndex: @escaping (Int) -> () = { _ in }) { 68 | self.itemSide = itemSide 69 | self._value = value 70 | self._isPresented = isPresented 71 | self.itemPadding = itemPadding 72 | self.visibleContentLength = visibleContentLength 73 | self.unitData = unitData 74 | self.onChangeActiveIndex = onChangeActiveIndex 75 | } 76 | } 77 | 78 | struct UnitsView_Previews: PreviewProvider { 79 | static var previews: some View { 80 | UnitsView(unitData: .init(), value: .constant(""), isPresented: .constant(false), onChangeActiveIndex: { _ in }) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/Extensions/Bundle+Ext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Bundle+Ext.swift 3 | // OnboardingAnimationOne 4 | // 5 | // Created by Boris on 01.09.2022. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Bundle { 11 | 12 | func decode(_ type: T.Type, 13 | from file: String, 14 | dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .deferredToDate, 15 | keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys) -> T { 16 | 17 | guard let url = self.url(forResource: file, withExtension: nil) else { 18 | fatalError("Failed to locate \(file) in bundle.") 19 | } 20 | 21 | guard let data = try? Data(contentsOf: url) else { 22 | fatalError("Failed to load \(file) from bundle.") 23 | } 24 | 25 | let decoder = JSONDecoder() 26 | decoder.dateDecodingStrategy = dateDecodingStrategy 27 | decoder.keyDecodingStrategy = keyDecodingStrategy 28 | 29 | do { 30 | return try decoder.decode(T.self, from: data) 31 | } catch DecodingError.keyNotFound(let key, let context) { 32 | fatalError("Failed to decode \(file) from bundle due to missing key '\(key.stringValue)' not found – \(context.debugDescription)") 33 | } catch DecodingError.typeMismatch(_, let context) { 34 | fatalError("Failed to decode \(file) from bundle due to type mismatch – \(context.debugDescription)") 35 | } catch DecodingError.valueNotFound(let type, let context) { 36 | fatalError("Failed to decode \(file) from bundle due to missing \(type) value – \(context.debugDescription)") 37 | } catch DecodingError.dataCorrupted(_) { 38 | fatalError("Failed to decode \(file) from bundle because it appears to be invalid JSON") 39 | } catch { 40 | fatalError("Failed to decode \(file) from bundle: \(error.localizedDescription)") 41 | } 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/Extensions/Color+Ext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Color + Ext.swift 3 | // OnboardingAnimationOne 4 | // 5 | // Created by Boris on 07.09.2022. 6 | // 7 | 8 | import SwiftUI 9 | 10 | extension Color { 11 | static var nightBlue = Color(UIColor(red: 0.216, green: 0.196, blue: 0.812, alpha: 1)) 12 | } 13 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/Extensions/View+Ext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // View+Ext.swift 3 | // OnboardingAnimationOne 4 | // 5 | // Created by Mariya Pankova on 19.07.2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | extension View { 11 | func frameModifier(_ contentLength: CGFloat, 12 | _ currentScrollOffset: CGFloat, 13 | _ orientation: Orientation) -> some View { 14 | modifier( 15 | FrameModifier( 16 | contentLength: contentLength, 17 | visibleContentLength: contentLength, 18 | currentScrollOffset: currentScrollOffset, 19 | orientation: orientation 20 | ) 21 | ) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/Resourсes/Assets.xcassets/App/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/Resourсes/Assets.xcassets/App/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/Resourсes/Assets.xcassets/App/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/Resourсes/Assets.xcassets/Card Images/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/Resourсes/Assets.xcassets/Card Images/card_image_01.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Illustration 1.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/Resourсes/Assets.xcassets/Card Images/card_image_01.imageset/Illustration 1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/Resourсes/Assets.xcassets/Card Images/card_image_02.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Illustration 2.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/Resourсes/Assets.xcassets/Card Images/card_image_02.imageset/Illustration 2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/Resourсes/Assets.xcassets/Card Images/card_image_03.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Illustration 3.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /OnboardingAnimationOne/Resourсes/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Paging scroll view SwiftUI 3 | Since there is not such option with SwiftUI ScrollView implementation, this is generic paging scrollView in SwiftUI 4 | 5 | ![Example](https://github.com/borisdipner/paging-srollview-swiftui/blob/main/example.gif) 6 | -------------------------------------------------------------------------------- /example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borisdipner/paging-srollview-swiftui/410b062050b295d79c01015bcfc172f3c645d7b2/example.gif --------------------------------------------------------------------------------