SwiftUI: Scroll like in UIKit using only SwiftUI _hidden APIs
Imagine you could configure the SwiftUI ScrollView
Imagine you could do pagination without introspection.
Imagine you could manipulate with
- content offset (programmatically scroll with animation to the defined offset)
- content insets
- indicator insets
- deceleration rate
- bouncing options
- enabling or disabling of scrolling
- interactive drag allowance
in other words, most of the configurable properties of UIScrollView
in UIKit, but with pure SwiftUI.
SwiftUI actually allows all these options to be configured, but in order to learn about it one would have to find out the hidden public SwiftUI types in the .swiftinterface
of SwiftUI.
Previously I wrote about another discovery of the hidden types in SwiftUI — the _ViewTest protocol.
This time, I prepared a special library SolidScroll which liberates _ScrollView
and _PagingView
of SwiftUI. It contains only type aliases to the hidden SwiftUI types, a couple of initializers for the convenience, and documentation in DocC.
Let’s learn more about this SwiftUI treasure.
_ScrollView
The hidden _ScrollView
type represents the scroll view that is configured with _ScrollViewConfig
and _ScrollableContentProvider.
public init<Provider>(contentProvider: Provider, config: _ScrollViewConfig = _ScrollViewConfig()) where Provider : _ScrollableContentProvider
In SolidScroll
library, the corresponding type aliases which make these types visible, are:
SolidScrollView
(alias for_ScrollView
)ScrollViewConfig
(alias for_ScrollViewConfig
)AligningContentProvider
(alias for_AligningContentProvider
)ContainedScrollViewKey
(preference key, alias for_ContainedScrollViewKey
)SolidScrollViewProxy
(alias for_ScrollViewProxy
)
The way of handling the SolidScrollView
is very similar to a regular ScrollViewReader
which you are probably already familiar with (or, in case if you’ve never heard of it, I recommend this story for reading), but instead of creating the reader, we are listening to the preference change of the ContainedScrollViewKey
preference.
Once we’ve got the preference’s value, and we have the scroll view proxy, we can call setContentOfsset(_:animated:completion:)
on it in order to scroll programmatically (with or without animation) to the concrete position in the scrollable content.
Also, now we have access to the properties and methods of the underlying scroll view:
_ScrollViewProxy
type in SwiftUI_PagingView
The hidden _PagingView
type represents the scroll view with preconfigured paging. The configuration for the paging is provided via _PagingViewConfig
.
In SolidScroll
library, the corresponding type aliases which make these types visible, are:
PagingView
(alias for_PagingView
)PagingViewConfig
(alias for_PagingViewConfig
)
The approach to handle PagingView
is very simple — just provide your paging view config and the collection of views which will be the pages of the PagingView
.
The possible limitation which I see here is that both PagingViewConfig
and ScrollViewConfig
only allow for all pages of the same size. If you need to have the pages of different size, you’ll have to get a bit creative.
I’ve recorded a small demo video for the SolidScroll library, check it out:
Thanks for reading!