Skip to content

Simultaneously scrolling ScrollViews with SwiftUI support! β†•οΈπŸš€

License

Notifications You must be signed in to change notification settings

stonko1994/SimultaneouslyScrollView

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

40 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

SimultaneouslyScrollView

Simultaneously scrolling ScrollViews with SwiftUI support


Build SwiftLint

Installation

Swift Package Manager is a tool for managing the distribution of Swift frameworks. It integrates with the Swift build system to automate the process of downloading, compiling, and linking dependencies.

Using Xcode

To integrate using Xcode 13, open your Project file and specify it in Project > Package Dependencies using the following URL:

https://github.com/stonko1994/SimultaneouslyScrollView.git

Usage

import SimultaneouslyScrollView

Synchronize multiple UIScrollViews

  1. Create an SimultaneouslyScrollViewHandler instance by using the factory method and the create function:
    let simultaneouslyScrollViewHandler = SimultaneouslyScrollViewHandlerFactory.create()
  2. Register UIScrollViews that should be synchronized:
    simultaneouslyScrollViewHandler.register(scrollView: scrollView)

SwiftUI support

To enable simultaneously scrolling in SwiftUI we need to utilize another library that allows access to the underlying UIScrollView for a SwiftUI.ScrollView.

SwiftUI-Introspect πŸš€

Synchronize multiple ScrollViews

  1. Follow the installataion steps from SwiftUI-Introspect

    Recommended is to use version 1.0.0 or higher.
    
  2. Import Introspect in addition to SimultaneouslyScrollView

    import SimultaneouslyScrollView
    import SwiftUIIntrospect
  3. Access the UIScrollView from your ScrollView and register it to the SimultaneouslyScrollViewHandler.

    ScrollView {
        ...
    }
    .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17)) {
        viewModel.simultaneouslyScrollViewHandler.register(scrollView: $0)
    }
  4. That's it πŸ₯³πŸŽ‰

    I recommend storing the simultaneouslyScrollViewHandler inside some view-model. E.g. an @ObservedObject or a @StateObject.

How it works

SwiftUI doesn't provide any API to specify the contentOffset for ScrollViews. Therefore we need to access the underlying UIKit element and set the contentOffset there. This is where SwiftUI-Introspect comes in handy by providing access to the UIKit elements.

As every redraw of the View creates a new ScrollView and a new UIScrollView instance, it is important not to store strong references of the registered UIScrollViews. The SimultaneouslyScrollViewHandler manages this using a custom implementation using the WeakObjectStore.

When a ScrollView is scrolled by the user, the SimultaneouslyScrollViewHandler gets notified about this via the UIScrollViewDelegate. When this happens, the contentOffset of every other registered UIScrollView will be adapted to the new contentOffset of the currently scrolled UIScrollView.

Example

I use this package in one of my own Apps that is currently in the Appstore. So there shouldn't be any issues using this in any production code except the possibility that new SwiftUI versions could break SwiftUI-Introspect.

Please note that this introspection method might break in future SwiftUI releases. Future implementations might not use the same hierarchy, or might not use UIKit elements that are being looked for. Though the library is unlikely to crash, the .introspect() method will not be called in those cases.

Download Scoretastic πŸ₯³


Buy Me A Coffee