Simultaneously scrolling ScrollView
s with SwiftUI
support
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.
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
import SimultaneouslyScrollView
UIScrollView
s1) Create an SimultaneouslyScrollViewHandler
instance by using the factory method and the create function:
let simultaneouslyScrollViewHandler = SimultaneouslyScrollViewHandlerFactory.create()
1) Register UIScrollViews
that should be synchronized:
simultaneouslyScrollViewHandler.register(scrollView: scrollView)
To enable simultaneously scrolling in SwiftUI
we need to utilize another library that allows access to the underlying UIScrollView
for a SwiftUI.ScrollView
.
ScrollView
s1) Follow the installataion steps from SwiftUI-Introspect
Recommended is to use version 1.0.0 or higher.
1) Import Introspect
in addition to SimultaneouslyScrollView
import SimultaneouslyScrollView
import SwiftUIIntrospect
1) 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)
}
1) That's it 🥳🎉
I recommend storing the `simultaneouslyScrollViewHandler` inside some view-model. E.g. an `@ObservedObject` or a `@StateObject`.
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 UIScrollView
s. 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
.
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 🥳