├── .gitignore ├── FadingScrollerDemo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ └── Package.resolved └── xcuserdata │ └── scottandrew.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── FadingScrollerDemo ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── Image 1.imageset │ │ ├── Contents.json │ │ └── arno-smit-sKJ7zSylUao-unsplash.jpg │ ├── Image 2.imageset │ │ ├── Contents.json │ │ └── gatis-marcinkevics-a5uptAdUmjE-unsplash.jpg │ ├── Image 3.imageset │ │ ├── Contents.json │ │ └── rodion-kutsaiev-IJ25m7fXqtk-unsplash.jpg │ └── Image 4.imageset │ │ ├── Contents.json │ │ └── xiaoxiao-sun-e8e4YY65sOk-unsplash.jpg ├── BackgroundCalculator.swift ├── Extensions │ ├── UIColor+interpolate.swift │ └── UIImage+AverageColor.swift ├── FadingScrollerDemoApp.swift ├── GalleryView.swift ├── ImageEntry.swift ├── ImageItemView.swift ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json └── ScrollViewOffsetPreferenceKey .swift ├── FadingScrollerDemoTests └── FadingScrollerDemoTests.swift ├── FadingScrollerDemoUITests ├── FadingScrollerDemoUITests.swift └── FadingScrollerDemoUITestsLaunchTests.swift └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | # Package.pins 42 | # Package.resolved 43 | # *.xcodeproj 44 | # 45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 46 | # hence it is not needed unless you have added a package configuration file to your project 47 | # .swiftpm 48 | 49 | .build/ 50 | 51 | # CocoaPods 52 | # 53 | # We recommend against adding the Pods directory to your .gitignore. However 54 | # you should judge for yourself, the pros and cons are mentioned at: 55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 56 | # 57 | # Pods/ 58 | # 59 | # Add this line if you want to avoid checking in source code from the Xcode workspace 60 | # *.xcworkspace 61 | 62 | # Carthage 63 | # 64 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 65 | # Carthage/Checkouts 66 | 67 | Carthage/Build/ 68 | 69 | # Accio dependency management 70 | Dependencies/ 71 | .accio/ 72 | 73 | # fastlane 74 | # 75 | # It is recommended to not store the screenshots in the git repo. 76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 77 | # For more information about the recommended setup visit: 78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 79 | 80 | fastlane/report.xml 81 | fastlane/Preview.html 82 | fastlane/screenshots/**/*.png 83 | fastlane/test_output 84 | 85 | # Code Injection 86 | # 87 | # After new code Injection tools there's a generated folder /iOSInjectionProject 88 | # https://github.com/johnno1962/injectionforxcode 89 | 90 | iOSInjectionProject/ 91 | -------------------------------------------------------------------------------- /FadingScrollerDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 56; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 40C29AF028EE725800820505 /* FadingScrollerDemoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40C29AEF28EE725800820505 /* FadingScrollerDemoApp.swift */; }; 11 | 40C29AF228EE725800820505 /* GalleryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40C29AF128EE725800820505 /* GalleryView.swift */; }; 12 | 40C29AF428EE725900820505 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 40C29AF328EE725900820505 /* Assets.xcassets */; }; 13 | 40C29AF728EE725900820505 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 40C29AF628EE725900820505 /* Preview Assets.xcassets */; }; 14 | 40C29B0128EE725900820505 /* FadingScrollerDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40C29B0028EE725900820505 /* FadingScrollerDemoTests.swift */; }; 15 | 40C29B0B28EE725900820505 /* FadingScrollerDemoUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40C29B0A28EE725900820505 /* FadingScrollerDemoUITests.swift */; }; 16 | 40C29B0D28EE725900820505 /* FadingScrollerDemoUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40C29B0C28EE725900820505 /* FadingScrollerDemoUITestsLaunchTests.swift */; }; 17 | 40C29B1B28EE72A100820505 /* UIImage+AverageColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40C29B1A28EE72A100820505 /* UIImage+AverageColor.swift */; }; 18 | 40C29B1D28F1ED1400820505 /* ImageItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40C29B1C28F1ED1400820505 /* ImageItemView.swift */; }; 19 | 40C29B2028F2722D00820505 /* Introspect in Frameworks */ = {isa = PBXBuildFile; productRef = 40C29B1F28F2722D00820505 /* Introspect */; }; 20 | 40C29B2228F4691200820505 /* UIColor+interpolate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40C29B2128F4691200820505 /* UIColor+interpolate.swift */; }; 21 | 40C29B2428F4A73800820505 /* ImageEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40C29B2328F4A73800820505 /* ImageEntry.swift */; }; 22 | 40C29B2628F4A84900820505 /* ScrollViewOffsetPreferenceKey .swift in Sources */ = {isa = PBXBuildFile; fileRef = 40C29B2528F4A84900820505 /* ScrollViewOffsetPreferenceKey .swift */; }; 23 | 40C29B2828F4AB6E00820505 /* BackgroundCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40C29B2728F4AB6E00820505 /* BackgroundCalculator.swift */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXContainerItemProxy section */ 27 | 40C29AFD28EE725900820505 /* PBXContainerItemProxy */ = { 28 | isa = PBXContainerItemProxy; 29 | containerPortal = 40C29AE428EE725800820505 /* Project object */; 30 | proxyType = 1; 31 | remoteGlobalIDString = 40C29AEB28EE725800820505; 32 | remoteInfo = FadingScrollerDemo; 33 | }; 34 | 40C29B0728EE725900820505 /* PBXContainerItemProxy */ = { 35 | isa = PBXContainerItemProxy; 36 | containerPortal = 40C29AE428EE725800820505 /* Project object */; 37 | proxyType = 1; 38 | remoteGlobalIDString = 40C29AEB28EE725800820505; 39 | remoteInfo = FadingScrollerDemo; 40 | }; 41 | /* End PBXContainerItemProxy section */ 42 | 43 | /* Begin PBXFileReference section */ 44 | 40C29AEC28EE725800820505 /* FadingScrollerDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FadingScrollerDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 45 | 40C29AEF28EE725800820505 /* FadingScrollerDemoApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FadingScrollerDemoApp.swift; sourceTree = ""; }; 46 | 40C29AF128EE725800820505 /* GalleryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryView.swift; sourceTree = ""; }; 47 | 40C29AF328EE725900820505 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | 40C29AF628EE725900820505 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 49 | 40C29AFC28EE725900820505 /* FadingScrollerDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FadingScrollerDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 50 | 40C29B0028EE725900820505 /* FadingScrollerDemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FadingScrollerDemoTests.swift; sourceTree = ""; }; 51 | 40C29B0628EE725900820505 /* FadingScrollerDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FadingScrollerDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | 40C29B0A28EE725900820505 /* FadingScrollerDemoUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FadingScrollerDemoUITests.swift; sourceTree = ""; }; 53 | 40C29B0C28EE725900820505 /* FadingScrollerDemoUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FadingScrollerDemoUITestsLaunchTests.swift; sourceTree = ""; }; 54 | 40C29B1A28EE72A100820505 /* UIImage+AverageColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+AverageColor.swift"; sourceTree = ""; }; 55 | 40C29B1C28F1ED1400820505 /* ImageItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageItemView.swift; sourceTree = ""; }; 56 | 40C29B2128F4691200820505 /* UIColor+interpolate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+interpolate.swift"; sourceTree = ""; }; 57 | 40C29B2328F4A73800820505 /* ImageEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageEntry.swift; sourceTree = ""; }; 58 | 40C29B2528F4A84900820505 /* ScrollViewOffsetPreferenceKey .swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ScrollViewOffsetPreferenceKey .swift"; sourceTree = ""; }; 59 | 40C29B2728F4AB6E00820505 /* BackgroundCalculator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundCalculator.swift; sourceTree = ""; }; 60 | /* End PBXFileReference section */ 61 | 62 | /* Begin PBXFrameworksBuildPhase section */ 63 | 40C29AE928EE725800820505 /* Frameworks */ = { 64 | isa = PBXFrameworksBuildPhase; 65 | buildActionMask = 2147483647; 66 | files = ( 67 | 40C29B2028F2722D00820505 /* Introspect in Frameworks */, 68 | ); 69 | runOnlyForDeploymentPostprocessing = 0; 70 | }; 71 | 40C29AF928EE725900820505 /* Frameworks */ = { 72 | isa = PBXFrameworksBuildPhase; 73 | buildActionMask = 2147483647; 74 | files = ( 75 | ); 76 | runOnlyForDeploymentPostprocessing = 0; 77 | }; 78 | 40C29B0328EE725900820505 /* Frameworks */ = { 79 | isa = PBXFrameworksBuildPhase; 80 | buildActionMask = 2147483647; 81 | files = ( 82 | ); 83 | runOnlyForDeploymentPostprocessing = 0; 84 | }; 85 | /* End PBXFrameworksBuildPhase section */ 86 | 87 | /* Begin PBXGroup section */ 88 | 40C29AE328EE725800820505 = { 89 | isa = PBXGroup; 90 | children = ( 91 | 40C29AEE28EE725800820505 /* FadingScrollerDemo */, 92 | 40C29AFF28EE725900820505 /* FadingScrollerDemoTests */, 93 | 40C29B0928EE725900820505 /* FadingScrollerDemoUITests */, 94 | 40C29AED28EE725800820505 /* Products */, 95 | ); 96 | sourceTree = ""; 97 | }; 98 | 40C29AED28EE725800820505 /* Products */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | 40C29AEC28EE725800820505 /* FadingScrollerDemo.app */, 102 | 40C29AFC28EE725900820505 /* FadingScrollerDemoTests.xctest */, 103 | 40C29B0628EE725900820505 /* FadingScrollerDemoUITests.xctest */, 104 | ); 105 | name = Products; 106 | sourceTree = ""; 107 | }; 108 | 40C29AEE28EE725800820505 /* FadingScrollerDemo */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | 40C29B1928EE726600820505 /* Extensions */, 112 | 40C29B2528F4A84900820505 /* ScrollViewOffsetPreferenceKey .swift */, 113 | 40C29AEF28EE725800820505 /* FadingScrollerDemoApp.swift */, 114 | 40C29AF128EE725800820505 /* GalleryView.swift */, 115 | 40C29AF328EE725900820505 /* Assets.xcassets */, 116 | 40C29AF528EE725900820505 /* Preview Content */, 117 | 40C29B1C28F1ED1400820505 /* ImageItemView.swift */, 118 | 40C29B2328F4A73800820505 /* ImageEntry.swift */, 119 | 40C29B2728F4AB6E00820505 /* BackgroundCalculator.swift */, 120 | ); 121 | path = FadingScrollerDemo; 122 | sourceTree = ""; 123 | }; 124 | 40C29AF528EE725900820505 /* Preview Content */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 40C29AF628EE725900820505 /* Preview Assets.xcassets */, 128 | ); 129 | path = "Preview Content"; 130 | sourceTree = ""; 131 | }; 132 | 40C29AFF28EE725900820505 /* FadingScrollerDemoTests */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | 40C29B0028EE725900820505 /* FadingScrollerDemoTests.swift */, 136 | ); 137 | path = FadingScrollerDemoTests; 138 | sourceTree = ""; 139 | }; 140 | 40C29B0928EE725900820505 /* FadingScrollerDemoUITests */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | 40C29B0A28EE725900820505 /* FadingScrollerDemoUITests.swift */, 144 | 40C29B0C28EE725900820505 /* FadingScrollerDemoUITestsLaunchTests.swift */, 145 | ); 146 | path = FadingScrollerDemoUITests; 147 | sourceTree = ""; 148 | }; 149 | 40C29B1928EE726600820505 /* Extensions */ = { 150 | isa = PBXGroup; 151 | children = ( 152 | 40C29B1A28EE72A100820505 /* UIImage+AverageColor.swift */, 153 | 40C29B2128F4691200820505 /* UIColor+interpolate.swift */, 154 | ); 155 | path = Extensions; 156 | sourceTree = ""; 157 | }; 158 | /* End PBXGroup section */ 159 | 160 | /* Begin PBXNativeTarget section */ 161 | 40C29AEB28EE725800820505 /* FadingScrollerDemo */ = { 162 | isa = PBXNativeTarget; 163 | buildConfigurationList = 40C29B1028EE725900820505 /* Build configuration list for PBXNativeTarget "FadingScrollerDemo" */; 164 | buildPhases = ( 165 | 40C29AE828EE725800820505 /* Sources */, 166 | 40C29AE928EE725800820505 /* Frameworks */, 167 | 40C29AEA28EE725800820505 /* Resources */, 168 | ); 169 | buildRules = ( 170 | ); 171 | dependencies = ( 172 | ); 173 | name = FadingScrollerDemo; 174 | packageProductDependencies = ( 175 | 40C29B1F28F2722D00820505 /* Introspect */, 176 | ); 177 | productName = FadingScrollerDemo; 178 | productReference = 40C29AEC28EE725800820505 /* FadingScrollerDemo.app */; 179 | productType = "com.apple.product-type.application"; 180 | }; 181 | 40C29AFB28EE725900820505 /* FadingScrollerDemoTests */ = { 182 | isa = PBXNativeTarget; 183 | buildConfigurationList = 40C29B1328EE725900820505 /* Build configuration list for PBXNativeTarget "FadingScrollerDemoTests" */; 184 | buildPhases = ( 185 | 40C29AF828EE725900820505 /* Sources */, 186 | 40C29AF928EE725900820505 /* Frameworks */, 187 | 40C29AFA28EE725900820505 /* Resources */, 188 | ); 189 | buildRules = ( 190 | ); 191 | dependencies = ( 192 | 40C29AFE28EE725900820505 /* PBXTargetDependency */, 193 | ); 194 | name = FadingScrollerDemoTests; 195 | productName = FadingScrollerDemoTests; 196 | productReference = 40C29AFC28EE725900820505 /* FadingScrollerDemoTests.xctest */; 197 | productType = "com.apple.product-type.bundle.unit-test"; 198 | }; 199 | 40C29B0528EE725900820505 /* FadingScrollerDemoUITests */ = { 200 | isa = PBXNativeTarget; 201 | buildConfigurationList = 40C29B1628EE725900820505 /* Build configuration list for PBXNativeTarget "FadingScrollerDemoUITests" */; 202 | buildPhases = ( 203 | 40C29B0228EE725900820505 /* Sources */, 204 | 40C29B0328EE725900820505 /* Frameworks */, 205 | 40C29B0428EE725900820505 /* Resources */, 206 | ); 207 | buildRules = ( 208 | ); 209 | dependencies = ( 210 | 40C29B0828EE725900820505 /* PBXTargetDependency */, 211 | ); 212 | name = FadingScrollerDemoUITests; 213 | productName = FadingScrollerDemoUITests; 214 | productReference = 40C29B0628EE725900820505 /* FadingScrollerDemoUITests.xctest */; 215 | productType = "com.apple.product-type.bundle.ui-testing"; 216 | }; 217 | /* End PBXNativeTarget section */ 218 | 219 | /* Begin PBXProject section */ 220 | 40C29AE428EE725800820505 /* Project object */ = { 221 | isa = PBXProject; 222 | attributes = { 223 | BuildIndependentTargetsInParallel = 1; 224 | LastSwiftUpdateCheck = 1410; 225 | LastUpgradeCheck = 1410; 226 | TargetAttributes = { 227 | 40C29AEB28EE725800820505 = { 228 | CreatedOnToolsVersion = 14.1; 229 | }; 230 | 40C29AFB28EE725900820505 = { 231 | CreatedOnToolsVersion = 14.1; 232 | TestTargetID = 40C29AEB28EE725800820505; 233 | }; 234 | 40C29B0528EE725900820505 = { 235 | CreatedOnToolsVersion = 14.1; 236 | TestTargetID = 40C29AEB28EE725800820505; 237 | }; 238 | }; 239 | }; 240 | buildConfigurationList = 40C29AE728EE725800820505 /* Build configuration list for PBXProject "FadingScrollerDemo" */; 241 | compatibilityVersion = "Xcode 14.0"; 242 | developmentRegion = en; 243 | hasScannedForEncodings = 0; 244 | knownRegions = ( 245 | en, 246 | Base, 247 | ); 248 | mainGroup = 40C29AE328EE725800820505; 249 | packageReferences = ( 250 | 40C29B1E28F2722D00820505 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */, 251 | ); 252 | productRefGroup = 40C29AED28EE725800820505 /* Products */; 253 | projectDirPath = ""; 254 | projectRoot = ""; 255 | targets = ( 256 | 40C29AEB28EE725800820505 /* FadingScrollerDemo */, 257 | 40C29AFB28EE725900820505 /* FadingScrollerDemoTests */, 258 | 40C29B0528EE725900820505 /* FadingScrollerDemoUITests */, 259 | ); 260 | }; 261 | /* End PBXProject section */ 262 | 263 | /* Begin PBXResourcesBuildPhase section */ 264 | 40C29AEA28EE725800820505 /* Resources */ = { 265 | isa = PBXResourcesBuildPhase; 266 | buildActionMask = 2147483647; 267 | files = ( 268 | 40C29AF728EE725900820505 /* Preview Assets.xcassets in Resources */, 269 | 40C29AF428EE725900820505 /* Assets.xcassets in Resources */, 270 | ); 271 | runOnlyForDeploymentPostprocessing = 0; 272 | }; 273 | 40C29AFA28EE725900820505 /* Resources */ = { 274 | isa = PBXResourcesBuildPhase; 275 | buildActionMask = 2147483647; 276 | files = ( 277 | ); 278 | runOnlyForDeploymentPostprocessing = 0; 279 | }; 280 | 40C29B0428EE725900820505 /* Resources */ = { 281 | isa = PBXResourcesBuildPhase; 282 | buildActionMask = 2147483647; 283 | files = ( 284 | ); 285 | runOnlyForDeploymentPostprocessing = 0; 286 | }; 287 | /* End PBXResourcesBuildPhase section */ 288 | 289 | /* Begin PBXSourcesBuildPhase section */ 290 | 40C29AE828EE725800820505 /* Sources */ = { 291 | isa = PBXSourcesBuildPhase; 292 | buildActionMask = 2147483647; 293 | files = ( 294 | 40C29B2628F4A84900820505 /* ScrollViewOffsetPreferenceKey .swift in Sources */, 295 | 40C29B1B28EE72A100820505 /* UIImage+AverageColor.swift in Sources */, 296 | 40C29AF228EE725800820505 /* GalleryView.swift in Sources */, 297 | 40C29B2228F4691200820505 /* UIColor+interpolate.swift in Sources */, 298 | 40C29AF028EE725800820505 /* FadingScrollerDemoApp.swift in Sources */, 299 | 40C29B2428F4A73800820505 /* ImageEntry.swift in Sources */, 300 | 40C29B1D28F1ED1400820505 /* ImageItemView.swift in Sources */, 301 | 40C29B2828F4AB6E00820505 /* BackgroundCalculator.swift in Sources */, 302 | ); 303 | runOnlyForDeploymentPostprocessing = 0; 304 | }; 305 | 40C29AF828EE725900820505 /* Sources */ = { 306 | isa = PBXSourcesBuildPhase; 307 | buildActionMask = 2147483647; 308 | files = ( 309 | 40C29B0128EE725900820505 /* FadingScrollerDemoTests.swift in Sources */, 310 | ); 311 | runOnlyForDeploymentPostprocessing = 0; 312 | }; 313 | 40C29B0228EE725900820505 /* Sources */ = { 314 | isa = PBXSourcesBuildPhase; 315 | buildActionMask = 2147483647; 316 | files = ( 317 | 40C29B0D28EE725900820505 /* FadingScrollerDemoUITestsLaunchTests.swift in Sources */, 318 | 40C29B0B28EE725900820505 /* FadingScrollerDemoUITests.swift in Sources */, 319 | ); 320 | runOnlyForDeploymentPostprocessing = 0; 321 | }; 322 | /* End PBXSourcesBuildPhase section */ 323 | 324 | /* Begin PBXTargetDependency section */ 325 | 40C29AFE28EE725900820505 /* PBXTargetDependency */ = { 326 | isa = PBXTargetDependency; 327 | target = 40C29AEB28EE725800820505 /* FadingScrollerDemo */; 328 | targetProxy = 40C29AFD28EE725900820505 /* PBXContainerItemProxy */; 329 | }; 330 | 40C29B0828EE725900820505 /* PBXTargetDependency */ = { 331 | isa = PBXTargetDependency; 332 | target = 40C29AEB28EE725800820505 /* FadingScrollerDemo */; 333 | targetProxy = 40C29B0728EE725900820505 /* PBXContainerItemProxy */; 334 | }; 335 | /* End PBXTargetDependency section */ 336 | 337 | /* Begin XCBuildConfiguration section */ 338 | 40C29B0E28EE725900820505 /* Debug */ = { 339 | isa = XCBuildConfiguration; 340 | buildSettings = { 341 | ALWAYS_SEARCH_USER_PATHS = NO; 342 | CLANG_ANALYZER_NONNULL = YES; 343 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 344 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 345 | CLANG_ENABLE_MODULES = YES; 346 | CLANG_ENABLE_OBJC_ARC = YES; 347 | CLANG_ENABLE_OBJC_WEAK = YES; 348 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 349 | CLANG_WARN_BOOL_CONVERSION = YES; 350 | CLANG_WARN_COMMA = YES; 351 | CLANG_WARN_CONSTANT_CONVERSION = YES; 352 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 353 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 354 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 355 | CLANG_WARN_EMPTY_BODY = YES; 356 | CLANG_WARN_ENUM_CONVERSION = YES; 357 | CLANG_WARN_INFINITE_RECURSION = YES; 358 | CLANG_WARN_INT_CONVERSION = YES; 359 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 360 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 361 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 362 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 363 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 364 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 365 | CLANG_WARN_STRICT_PROTOTYPES = YES; 366 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 367 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 368 | CLANG_WARN_UNREACHABLE_CODE = YES; 369 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 370 | COPY_PHASE_STRIP = NO; 371 | DEBUG_INFORMATION_FORMAT = dwarf; 372 | ENABLE_STRICT_OBJC_MSGSEND = YES; 373 | ENABLE_TESTABILITY = YES; 374 | GCC_C_LANGUAGE_STANDARD = gnu11; 375 | GCC_DYNAMIC_NO_PIC = NO; 376 | GCC_NO_COMMON_BLOCKS = YES; 377 | GCC_OPTIMIZATION_LEVEL = 0; 378 | GCC_PREPROCESSOR_DEFINITIONS = ( 379 | "DEBUG=1", 380 | "$(inherited)", 381 | ); 382 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 383 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 384 | GCC_WARN_UNDECLARED_SELECTOR = YES; 385 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 386 | GCC_WARN_UNUSED_FUNCTION = YES; 387 | GCC_WARN_UNUSED_VARIABLE = YES; 388 | IPHONEOS_DEPLOYMENT_TARGET = 16.1; 389 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 390 | MTL_FAST_MATH = YES; 391 | ONLY_ACTIVE_ARCH = YES; 392 | SDKROOT = iphoneos; 393 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 394 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 395 | }; 396 | name = Debug; 397 | }; 398 | 40C29B0F28EE725900820505 /* Release */ = { 399 | isa = XCBuildConfiguration; 400 | buildSettings = { 401 | ALWAYS_SEARCH_USER_PATHS = NO; 402 | CLANG_ANALYZER_NONNULL = YES; 403 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 404 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 405 | CLANG_ENABLE_MODULES = YES; 406 | CLANG_ENABLE_OBJC_ARC = YES; 407 | CLANG_ENABLE_OBJC_WEAK = YES; 408 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 409 | CLANG_WARN_BOOL_CONVERSION = YES; 410 | CLANG_WARN_COMMA = YES; 411 | CLANG_WARN_CONSTANT_CONVERSION = YES; 412 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 413 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 414 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 415 | CLANG_WARN_EMPTY_BODY = YES; 416 | CLANG_WARN_ENUM_CONVERSION = YES; 417 | CLANG_WARN_INFINITE_RECURSION = YES; 418 | CLANG_WARN_INT_CONVERSION = YES; 419 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 420 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 421 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 422 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 423 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 424 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 425 | CLANG_WARN_STRICT_PROTOTYPES = YES; 426 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 427 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 428 | CLANG_WARN_UNREACHABLE_CODE = YES; 429 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 430 | COPY_PHASE_STRIP = NO; 431 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 432 | ENABLE_NS_ASSERTIONS = NO; 433 | ENABLE_STRICT_OBJC_MSGSEND = YES; 434 | GCC_C_LANGUAGE_STANDARD = gnu11; 435 | GCC_NO_COMMON_BLOCKS = YES; 436 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 437 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 438 | GCC_WARN_UNDECLARED_SELECTOR = YES; 439 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 440 | GCC_WARN_UNUSED_FUNCTION = YES; 441 | GCC_WARN_UNUSED_VARIABLE = YES; 442 | IPHONEOS_DEPLOYMENT_TARGET = 16.1; 443 | MTL_ENABLE_DEBUG_INFO = NO; 444 | MTL_FAST_MATH = YES; 445 | SDKROOT = iphoneos; 446 | SWIFT_COMPILATION_MODE = wholemodule; 447 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 448 | VALIDATE_PRODUCT = YES; 449 | }; 450 | name = Release; 451 | }; 452 | 40C29B1128EE725900820505 /* Debug */ = { 453 | isa = XCBuildConfiguration; 454 | buildSettings = { 455 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 456 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 457 | CODE_SIGN_STYLE = Automatic; 458 | CURRENT_PROJECT_VERSION = 1; 459 | DEVELOPMENT_ASSET_PATHS = "\"FadingScrollerDemo/Preview Content\""; 460 | DEVELOPMENT_TEAM = L4N82R5CV4; 461 | ENABLE_PREVIEWS = YES; 462 | GENERATE_INFOPLIST_FILE = YES; 463 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 464 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 465 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 466 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 467 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 468 | LD_RUNPATH_SEARCH_PATHS = ( 469 | "$(inherited)", 470 | "@executable_path/Frameworks", 471 | ); 472 | MARKETING_VERSION = 1.0; 473 | PRODUCT_BUNDLE_IDENTIFIER = com.newWaveDigital.FadingScrollerDemo; 474 | PRODUCT_NAME = "$(TARGET_NAME)"; 475 | SWIFT_EMIT_LOC_STRINGS = YES; 476 | SWIFT_VERSION = 5.0; 477 | TARGETED_DEVICE_FAMILY = "1,2"; 478 | }; 479 | name = Debug; 480 | }; 481 | 40C29B1228EE725900820505 /* Release */ = { 482 | isa = XCBuildConfiguration; 483 | buildSettings = { 484 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 485 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 486 | CODE_SIGN_STYLE = Automatic; 487 | CURRENT_PROJECT_VERSION = 1; 488 | DEVELOPMENT_ASSET_PATHS = "\"FadingScrollerDemo/Preview Content\""; 489 | DEVELOPMENT_TEAM = L4N82R5CV4; 490 | ENABLE_PREVIEWS = YES; 491 | GENERATE_INFOPLIST_FILE = YES; 492 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 493 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 494 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 495 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 496 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 497 | LD_RUNPATH_SEARCH_PATHS = ( 498 | "$(inherited)", 499 | "@executable_path/Frameworks", 500 | ); 501 | MARKETING_VERSION = 1.0; 502 | PRODUCT_BUNDLE_IDENTIFIER = com.newWaveDigital.FadingScrollerDemo; 503 | PRODUCT_NAME = "$(TARGET_NAME)"; 504 | SWIFT_EMIT_LOC_STRINGS = YES; 505 | SWIFT_VERSION = 5.0; 506 | TARGETED_DEVICE_FAMILY = "1,2"; 507 | }; 508 | name = Release; 509 | }; 510 | 40C29B1428EE725900820505 /* Debug */ = { 511 | isa = XCBuildConfiguration; 512 | buildSettings = { 513 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 514 | BUNDLE_LOADER = "$(TEST_HOST)"; 515 | CODE_SIGN_STYLE = Automatic; 516 | CURRENT_PROJECT_VERSION = 1; 517 | DEVELOPMENT_TEAM = L4N82R5CV4; 518 | GENERATE_INFOPLIST_FILE = YES; 519 | IPHONEOS_DEPLOYMENT_TARGET = 16.1; 520 | MARKETING_VERSION = 1.0; 521 | PRODUCT_BUNDLE_IDENTIFIER = com.newWaveDigital.FadingScrollerDemoTests; 522 | PRODUCT_NAME = "$(TARGET_NAME)"; 523 | SWIFT_EMIT_LOC_STRINGS = NO; 524 | SWIFT_VERSION = 5.0; 525 | TARGETED_DEVICE_FAMILY = "1,2"; 526 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FadingScrollerDemo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/FadingScrollerDemo"; 527 | }; 528 | name = Debug; 529 | }; 530 | 40C29B1528EE725900820505 /* Release */ = { 531 | isa = XCBuildConfiguration; 532 | buildSettings = { 533 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 534 | BUNDLE_LOADER = "$(TEST_HOST)"; 535 | CODE_SIGN_STYLE = Automatic; 536 | CURRENT_PROJECT_VERSION = 1; 537 | DEVELOPMENT_TEAM = L4N82R5CV4; 538 | GENERATE_INFOPLIST_FILE = YES; 539 | IPHONEOS_DEPLOYMENT_TARGET = 16.1; 540 | MARKETING_VERSION = 1.0; 541 | PRODUCT_BUNDLE_IDENTIFIER = com.newWaveDigital.FadingScrollerDemoTests; 542 | PRODUCT_NAME = "$(TARGET_NAME)"; 543 | SWIFT_EMIT_LOC_STRINGS = NO; 544 | SWIFT_VERSION = 5.0; 545 | TARGETED_DEVICE_FAMILY = "1,2"; 546 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FadingScrollerDemo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/FadingScrollerDemo"; 547 | }; 548 | name = Release; 549 | }; 550 | 40C29B1728EE725900820505 /* Debug */ = { 551 | isa = XCBuildConfiguration; 552 | buildSettings = { 553 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 554 | CODE_SIGN_STYLE = Automatic; 555 | CURRENT_PROJECT_VERSION = 1; 556 | DEVELOPMENT_TEAM = L4N82R5CV4; 557 | GENERATE_INFOPLIST_FILE = YES; 558 | MARKETING_VERSION = 1.0; 559 | PRODUCT_BUNDLE_IDENTIFIER = com.newWaveDigital.FadingScrollerDemoUITests; 560 | PRODUCT_NAME = "$(TARGET_NAME)"; 561 | SWIFT_EMIT_LOC_STRINGS = NO; 562 | SWIFT_VERSION = 5.0; 563 | TARGETED_DEVICE_FAMILY = "1,2"; 564 | TEST_TARGET_NAME = FadingScrollerDemo; 565 | }; 566 | name = Debug; 567 | }; 568 | 40C29B1828EE725900820505 /* Release */ = { 569 | isa = XCBuildConfiguration; 570 | buildSettings = { 571 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 572 | CODE_SIGN_STYLE = Automatic; 573 | CURRENT_PROJECT_VERSION = 1; 574 | DEVELOPMENT_TEAM = L4N82R5CV4; 575 | GENERATE_INFOPLIST_FILE = YES; 576 | MARKETING_VERSION = 1.0; 577 | PRODUCT_BUNDLE_IDENTIFIER = com.newWaveDigital.FadingScrollerDemoUITests; 578 | PRODUCT_NAME = "$(TARGET_NAME)"; 579 | SWIFT_EMIT_LOC_STRINGS = NO; 580 | SWIFT_VERSION = 5.0; 581 | TARGETED_DEVICE_FAMILY = "1,2"; 582 | TEST_TARGET_NAME = FadingScrollerDemo; 583 | }; 584 | name = Release; 585 | }; 586 | /* End XCBuildConfiguration section */ 587 | 588 | /* Begin XCConfigurationList section */ 589 | 40C29AE728EE725800820505 /* Build configuration list for PBXProject "FadingScrollerDemo" */ = { 590 | isa = XCConfigurationList; 591 | buildConfigurations = ( 592 | 40C29B0E28EE725900820505 /* Debug */, 593 | 40C29B0F28EE725900820505 /* Release */, 594 | ); 595 | defaultConfigurationIsVisible = 0; 596 | defaultConfigurationName = Release; 597 | }; 598 | 40C29B1028EE725900820505 /* Build configuration list for PBXNativeTarget "FadingScrollerDemo" */ = { 599 | isa = XCConfigurationList; 600 | buildConfigurations = ( 601 | 40C29B1128EE725900820505 /* Debug */, 602 | 40C29B1228EE725900820505 /* Release */, 603 | ); 604 | defaultConfigurationIsVisible = 0; 605 | defaultConfigurationName = Release; 606 | }; 607 | 40C29B1328EE725900820505 /* Build configuration list for PBXNativeTarget "FadingScrollerDemoTests" */ = { 608 | isa = XCConfigurationList; 609 | buildConfigurations = ( 610 | 40C29B1428EE725900820505 /* Debug */, 611 | 40C29B1528EE725900820505 /* Release */, 612 | ); 613 | defaultConfigurationIsVisible = 0; 614 | defaultConfigurationName = Release; 615 | }; 616 | 40C29B1628EE725900820505 /* Build configuration list for PBXNativeTarget "FadingScrollerDemoUITests" */ = { 617 | isa = XCConfigurationList; 618 | buildConfigurations = ( 619 | 40C29B1728EE725900820505 /* Debug */, 620 | 40C29B1828EE725900820505 /* Release */, 621 | ); 622 | defaultConfigurationIsVisible = 0; 623 | defaultConfigurationName = Release; 624 | }; 625 | /* End XCConfigurationList section */ 626 | 627 | /* Begin XCRemoteSwiftPackageReference section */ 628 | 40C29B1E28F2722D00820505 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */ = { 629 | isa = XCRemoteSwiftPackageReference; 630 | repositoryURL = "https://github.com/siteline/SwiftUI-Introspect.git"; 631 | requirement = { 632 | kind = upToNextMajorVersion; 633 | minimumVersion = 0.1.4; 634 | }; 635 | }; 636 | /* End XCRemoteSwiftPackageReference section */ 637 | 638 | /* Begin XCSwiftPackageProductDependency section */ 639 | 40C29B1F28F2722D00820505 /* Introspect */ = { 640 | isa = XCSwiftPackageProductDependency; 641 | package = 40C29B1E28F2722D00820505 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */; 642 | productName = Introspect; 643 | }; 644 | /* End XCSwiftPackageProductDependency section */ 645 | }; 646 | rootObject = 40C29AE428EE725800820505 /* Project object */; 647 | } 648 | -------------------------------------------------------------------------------- /FadingScrollerDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /FadingScrollerDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /FadingScrollerDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "pins" : [ 3 | { 4 | "identity" : "swiftui-introspect", 5 | "kind" : "remoteSourceControl", 6 | "location" : "https://github.com/siteline/SwiftUI-Introspect.git", 7 | "state" : { 8 | "revision" : "f2616860a41f9d9932da412a8978fec79c06fe24", 9 | "version" : "0.1.4" 10 | } 11 | } 12 | ], 13 | "version" : 2 14 | } 15 | -------------------------------------------------------------------------------- /FadingScrollerDemo.xcodeproj/xcuserdata/scottandrew.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 21 | 22 | 36 | 37 | 51 | 52 | 53 | 54 | 55 | 57 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /FadingScrollerDemo.xcodeproj/xcuserdata/scottandrew.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | FadingScrollerDemo.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /FadingScrollerDemo/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 | -------------------------------------------------------------------------------- /FadingScrollerDemo/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 | -------------------------------------------------------------------------------- /FadingScrollerDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /FadingScrollerDemo/Assets.xcassets/Image 1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "arno-smit-sKJ7zSylUao-unsplash.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /FadingScrollerDemo/Assets.xcassets/Image 1.imageset/arno-smit-sKJ7zSylUao-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottandrew/FadingScollerDemo/d368ee76f209facf580b947a0a04b88206bea092/FadingScrollerDemo/Assets.xcassets/Image 1.imageset/arno-smit-sKJ7zSylUao-unsplash.jpg -------------------------------------------------------------------------------- /FadingScrollerDemo/Assets.xcassets/Image 2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "gatis-marcinkevics-a5uptAdUmjE-unsplash.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /FadingScrollerDemo/Assets.xcassets/Image 2.imageset/gatis-marcinkevics-a5uptAdUmjE-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottandrew/FadingScollerDemo/d368ee76f209facf580b947a0a04b88206bea092/FadingScrollerDemo/Assets.xcassets/Image 2.imageset/gatis-marcinkevics-a5uptAdUmjE-unsplash.jpg -------------------------------------------------------------------------------- /FadingScrollerDemo/Assets.xcassets/Image 3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "rodion-kutsaiev-IJ25m7fXqtk-unsplash.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /FadingScrollerDemo/Assets.xcassets/Image 3.imageset/rodion-kutsaiev-IJ25m7fXqtk-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottandrew/FadingScollerDemo/d368ee76f209facf580b947a0a04b88206bea092/FadingScrollerDemo/Assets.xcassets/Image 3.imageset/rodion-kutsaiev-IJ25m7fXqtk-unsplash.jpg -------------------------------------------------------------------------------- /FadingScrollerDemo/Assets.xcassets/Image 4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "xiaoxiao-sun-e8e4YY65sOk-unsplash.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /FadingScrollerDemo/Assets.xcassets/Image 4.imageset/xiaoxiao-sun-e8e4YY65sOk-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottandrew/FadingScollerDemo/d368ee76f209facf580b947a0a04b88206bea092/FadingScrollerDemo/Assets.xcassets/Image 4.imageset/xiaoxiao-sun-e8e4YY65sOk-unsplash.jpg -------------------------------------------------------------------------------- /FadingScrollerDemo/BackgroundCalculator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BackgroundCalculator.swift 3 | // FadingScrollerDemo 4 | // 5 | // Created by Scott Andrew on 10/10/22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | class BackgroundCalculator: ObservableObject { 11 | @Published var color = Color.clear 12 | 13 | var width: CGFloat = 0 14 | var colors: [UIColor] = [] { 15 | didSet { 16 | let aColor = colors.count > 0 ? colors[0] : UIColor.clear 17 | color = Color(aColor) 18 | } 19 | } 20 | 21 | func calucateBackground(position: CGFloat) { 22 | guard width > 0, colors.count > 0 else { 23 | return 24 | } 25 | 26 | let fractionalPage = position / width 27 | let page = Int(fractionalPage) 28 | let fromColor: UIColor = colors[page] 29 | 30 | if fractionalPage > 0 && fractionalPage < CGFloat(colors.count - 1) { 31 | let nextPage = min(page + 1, colors.count - 1) 32 | let transitionPercentage = fractionalPage.truncatingRemainder(dividingBy: 1) 33 | 34 | color = Color(fromColor.interpolate(to: colors[nextPage], percentage: transitionPercentage)) 35 | } 36 | else { 37 | color = Color(fromColor) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /FadingScrollerDemo/Extensions/UIColor+interpolate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+interpolate.swift 3 | // FadingScrollerDemo 4 | // 5 | // Created by Scott Andrew on 10/10/22. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIColor { 11 | func getComponents() -> (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) { 12 | if let components = cgColor.components { 13 | switch components.count { 14 | case 2: 15 | return (red: components[0], green: components[0], blue: components[0], alpha: components[1]) 16 | case 4: 17 | return (red: components[0], green: components[1], blue: components[2], alpha: components[3]) 18 | default: 19 | break 20 | } 21 | } 22 | 23 | return (red: 0, green: 0, blue: 0, alpha: 0) 24 | } 25 | 26 | func interpolate(to: UIColor, percentage: CGFloat) -> UIColor { 27 | // lets get our image components.. We need to make sure 28 | let fromComponents = getComponents() 29 | let toComponents = to.getComponents() 30 | 31 | let newRed = (1.0 - percentage) * fromComponents.red + percentage * toComponents.red 32 | let newGreen = (1.0 - percentage) * fromComponents.green + percentage * toComponents.green 33 | let newBlue = (1.0 - percentage) * fromComponents.blue + percentage * toComponents.blue 34 | let newAlpha = (1.0 - percentage) * fromComponents.alpha + percentage * toComponents.alpha 35 | 36 | return UIColor(red: newRed, green: newGreen, blue: newBlue, alpha: newAlpha) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /FadingScrollerDemo/Extensions/UIImage+AverageColor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+AverageColor.swift 3 | // FadingTransitionScroll 4 | // 5 | // Created by Scott Andrew on 9/30/22. 6 | // 7 | // This code comes from https://christianselig.com/2021/04/efficient-average-color/ 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | extension UIImage { 13 | enum AverageColorAlgorithm { 14 | case simple 15 | case squareRoot 16 | } 17 | 18 | func findAverageColor(algorithm: AverageColorAlgorithm = .simple) -> UIColor? { 19 | guard let cgImage = cgImage else { return nil } 20 | 21 | // First, resize the image. We do this for two reasons, 1) less pixels to deal with means faster calculation and a resized image still has the "gist" of the colors, and 2) the image we're dealing with may come in any of a variety of color formats (CMYK, ARGB, RGBA, etc.) which complicates things, and redrawing it normalizes that into a base color format we can deal with. 22 | // 40x40 is a good size to resize to still preserve quite a bit of detail but not have too many pixels to deal with. Aspect ratio is irrelevant for just finding average color. 23 | let size = CGSize(width: 40, height: 40) 24 | 25 | let width = Int(size.width) 26 | let height = Int(size.height) 27 | let totalPixels = width * height 28 | 29 | let colorSpace = CGColorSpaceCreateDeviceRGB() 30 | 31 | // ARGB format 32 | let bitmapInfo: UInt32 = CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue 33 | 34 | // 8 bits for each color channel, we're doing ARGB so 32 bits (4 bytes) total, and thus if the image is n pixels wide, and has 4 bytes per pixel, the total bytes per row is 4n. That gives us 2^8 = 256 color variations for each RGB channel or 256 * 256 * 256 = ~16.7M color options in total. That seems like a lot, but lots of HDR movies are in 10 bit, which is (2^10)^3 = 1 billion color options! 35 | guard let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: width * 4, space: colorSpace, bitmapInfo: bitmapInfo) else { return nil } 36 | 37 | // Draw our resized image 38 | context.draw(cgImage, in: CGRect(origin: .zero, size: size)) 39 | 40 | guard let pixelBuffer = context.data else { return nil } 41 | 42 | // Bind the pixel buffer's memory location to a pointer we can use/access 43 | let pointer = pixelBuffer.bindMemory(to: UInt32.self, capacity: width * height) 44 | 45 | // Keep track of total colors (note: we don't care about alpha and will always assume alpha of 1, AKA opaque) 46 | var totalRed = 0 47 | var totalBlue = 0 48 | var totalGreen = 0 49 | 50 | // Column of pixels in image 51 | for x in 0 ..< width { 52 | // Row of pixels in image 53 | for y in 0 ..< height { 54 | // To get the pixel location just think of the image as a grid of pixels, but stored as one long row rather than columns and rows, so for instance to map the pixel from the grid in the 15th row and 3 columns in to our "long row", we'd offset ourselves 15 times the width in pixels of the image, and then offset by the amount of columns 55 | let pixel = pointer[(y * width) + x] 56 | 57 | let r = red(for: pixel) 58 | let g = green(for: pixel) 59 | let b = blue(for: pixel) 60 | 61 | switch algorithm { 62 | case .simple: 63 | totalRed += Int(r) 64 | totalBlue += Int(b) 65 | totalGreen += Int(g) 66 | case .squareRoot: 67 | totalRed += Int(pow(CGFloat(r), CGFloat(2))) 68 | totalGreen += Int(pow(CGFloat(g), CGFloat(2))) 69 | totalBlue += Int(pow(CGFloat(b), CGFloat(2))) 70 | } 71 | } 72 | } 73 | 74 | let averageRed: CGFloat 75 | let averageGreen: CGFloat 76 | let averageBlue: CGFloat 77 | 78 | switch algorithm { 79 | case .simple: 80 | averageRed = CGFloat(totalRed) / CGFloat(totalPixels) 81 | averageGreen = CGFloat(totalGreen) / CGFloat(totalPixels) 82 | averageBlue = CGFloat(totalBlue) / CGFloat(totalPixels) 83 | case .squareRoot: 84 | averageRed = sqrt(CGFloat(totalRed) / CGFloat(totalPixels)) 85 | averageGreen = sqrt(CGFloat(totalGreen) / CGFloat(totalPixels)) 86 | averageBlue = sqrt(CGFloat(totalBlue) / CGFloat(totalPixels)) 87 | } 88 | 89 | // Convert from [0 ... 255] format to the [0 ... 1.0] format UIColor wants 90 | return UIColor(red: averageRed / 255.0, green: averageGreen / 255.0, blue: averageBlue / 255.0, alpha: 1.0) 91 | } 92 | 93 | private func red(for pixelData: UInt32) -> UInt8 { 94 | return UInt8((pixelData >> 16) & 255) 95 | } 96 | 97 | private func green(for pixelData: UInt32) -> UInt8 { 98 | return UInt8((pixelData >> 8) & 255) 99 | } 100 | 101 | private func blue(for pixelData: UInt32) -> UInt8 { 102 | return UInt8((pixelData >> 0) & 255) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /FadingScrollerDemo/FadingScrollerDemoApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FadingScrollerDemoApp.swift 3 | // FadingScrollerDemo 4 | // 5 | // Created by Scott Andrew on 10/5/22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct FadingScrollerDemoApp: App { 12 | var body: some Scene { 13 | WindowGroup { 14 | GalleryView() 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /FadingScrollerDemo/GalleryView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // FadingScrollerDemo 4 | // 5 | // Created by Scott Andrew on 10/5/22. 6 | // 7 | 8 | import Introspect 9 | import SwiftUI 10 | 11 | struct GalleryView: View { 12 | @State var images = [ 13 | ImageEntry(imageName: "Image 1"), 14 | ImageEntry(imageName: "Image 2"), 15 | ImageEntry(imageName: "Image 3"), 16 | ImageEntry(imageName: "Image 4") 17 | ] 18 | 19 | @StateObject var backgroundCalculator = BackgroundCalculator() 20 | 21 | var body: some View { 22 | GeometryReader { g in 23 | ZStack { 24 | Rectangle().foregroundColor(backgroundCalculator.color) 25 | 26 | ScrollView(.horizontal) { 27 | ZStack { 28 | GeometryReader { proxy in 29 | let offset = proxy.frame(in: .named("scroll")).minX 30 | Color.clear.preference(key: ScrollViewOffsetPreferenceKey.self, value: offset) 31 | } 32 | 33 | LazyHStack(spacing: 0) { 34 | ForEach(images) { image in 35 | Image(uiImage: image.image) 36 | .resizable() 37 | .cornerRadius(15) 38 | .aspectRatio(contentMode: .fit) 39 | .padding(24) 40 | .frame(width: g.size.width, height: g.size.height) 41 | } 42 | } 43 | } 44 | } 45 | .coordinateSpace(name: "scroll") 46 | .introspectScrollView { scrollView in 47 | scrollView.isPagingEnabled = true 48 | } 49 | .onPreferenceChange(ScrollViewOffsetPreferenceKey.self) { value in 50 | backgroundCalculator.calucateBackground(position: value * -1) 51 | } 52 | } 53 | .onAppear { 54 | backgroundCalculator.width = g.size.width 55 | backgroundCalculator.colors = images.map { $0.color } 56 | } 57 | } 58 | .ignoresSafeArea() 59 | } 60 | } 61 | 62 | struct GalleryView_Previews: PreviewProvider { 63 | static var previews: some View { 64 | GalleryView() 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /FadingScrollerDemo/ImageEntry.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageEntry.swift 3 | // FadingScrollerDemo 4 | // 5 | // Created by Scott Andrew on 10/10/22. 6 | // 7 | 8 | import UIKit 9 | 10 | struct ImageEntry: Identifiable { 11 | let image: UIImage 12 | let id: UUID 13 | let color: UIColor 14 | 15 | init(imageName: String) { 16 | image = UIImage(named: imageName)! 17 | color = image.findAverageColor() ?? .clear 18 | id = UUID() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /FadingScrollerDemo/ImageItemView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageItemView.swift 3 | // FadingScrollerDemo 4 | // 5 | // Created by Scott Andrew on 10/8/22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ImageItemView: View { 11 | var body: some View { 12 | GeometryReader { geometry in 13 | 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /FadingScrollerDemo/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /FadingScrollerDemo/ScrollViewOffsetPreferenceKey .swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScrollViewOffsetPreferenceKey .swift 3 | // FadingScrollerDemo 4 | // 5 | // Created by Scott Andrew on 10/10/22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ScrollViewOffsetPreferenceKey: PreferenceKey { 11 | typealias Value = CGFloat 12 | static var defaultValue: CGFloat = .zero 13 | 14 | static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { 15 | value += nextValue() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /FadingScrollerDemoTests/FadingScrollerDemoTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FadingScrollerDemoTests.swift 3 | // FadingScrollerDemoTests 4 | // 5 | // Created by Scott Andrew on 10/5/22. 6 | // 7 | 8 | import XCTest 9 | @testable import FadingScrollerDemo 10 | 11 | final class FadingScrollerDemoTests: XCTestCase { 12 | 13 | override func setUpWithError() throws { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDownWithError() throws { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | } 20 | 21 | func testExample() throws { 22 | // This is an example of a functional test case. 23 | // Use XCTAssert and related functions to verify your tests produce the correct results. 24 | // Any test you write for XCTest can be annotated as throws and async. 25 | // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. 26 | // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. 27 | } 28 | 29 | func testPerformanceExample() throws { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /FadingScrollerDemoUITests/FadingScrollerDemoUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FadingScrollerDemoUITests.swift 3 | // FadingScrollerDemoUITests 4 | // 5 | // Created by Scott Andrew on 10/5/22. 6 | // 7 | 8 | import XCTest 9 | 10 | final class FadingScrollerDemoUITests: XCTestCase { 11 | 12 | override func setUpWithError() throws { 13 | // Put setup code here. This method is called before the invocation of each test method in the class. 14 | 15 | // In UI tests it is usually best to stop immediately when a failure occurs. 16 | continueAfterFailure = false 17 | 18 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 19 | } 20 | 21 | override func tearDownWithError() throws { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | func testExample() throws { 26 | // UI tests must launch the application that they test. 27 | let app = XCUIApplication() 28 | app.launch() 29 | 30 | // Use XCTAssert and related functions to verify your tests produce the correct results. 31 | } 32 | 33 | func testLaunchPerformance() throws { 34 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { 35 | // This measures how long it takes to launch your application. 36 | measure(metrics: [XCTApplicationLaunchMetric()]) { 37 | XCUIApplication().launch() 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /FadingScrollerDemoUITests/FadingScrollerDemoUITestsLaunchTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FadingScrollerDemoUITestsLaunchTests.swift 3 | // FadingScrollerDemoUITests 4 | // 5 | // Created by Scott Andrew on 10/5/22. 6 | // 7 | 8 | import XCTest 9 | 10 | final class FadingScrollerDemoUITestsLaunchTests: XCTestCase { 11 | 12 | override class var runsForEachTargetApplicationUIConfiguration: Bool { 13 | true 14 | } 15 | 16 | override func setUpWithError() throws { 17 | continueAfterFailure = false 18 | } 19 | 20 | func testLaunch() throws { 21 | let app = XCUIApplication() 22 | app.launch() 23 | 24 | // Insert steps here to perform after app launch but before taking a screenshot, 25 | // such as logging into a test account or navigating somewhere in the app 26 | 27 | let attachment = XCTAttachment(screenshot: app.screenshot()) 28 | attachment.name = "Launch Screen" 29 | attachment.lifetime = .keepAlways 30 | add(attachment) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Scott Andrew 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | --------------------------------------------------------------------------------