A liberated _ScrollView and _PagingView of SwiftUI.
SolidScroll
A liberated _ScrollView
and _PagingView
of SwiftUI.
SolidScroll allows to unlock the potential of the scroll view in SwiftUI.
Unlike the regular SwiftUI ScrollView
and ScrollViewProxy
, the hidden SwiftUI _ScrollView
and _ScrollViewProxy
types allow to get near-UIScrollView control over the status of scrolling in real time, content insets, animate content offset, and much more.
SolidScroll is a set of type aliases to SwiftUI hidden types, and couple of helper initializers for the convinience.
Also, the library contains docc-compatible documentation where each type alias is represented with the hidden interface of the aliased type.
SwiftUI has a hidden _ContainedScrollViewKey
type, which is actually a not-publicly-conformed PreferenceKey
type.
This preference reports the _ScrollViewProxy
where all functionality of the _ScrollView
is unlocked.
In order to get the proxy, simply create SolidScrollView
in the body
of your view, then store the proxy in a @State
, as shown in the following example:
import SolidScroll
struct ContentView: View {
var config = ScrollViewConfig()
@State var scrollViewProxy: SolidScrollViewProxy?
var body: some View {
VStack(spacing: 0) {
SolidScrollView(config) {
VStack(spacing: 0) {
Color.red.frame(height: 200)
Color.green.frame(height: 200)
Color.blue.frame(height: 200)
Color.black.frame(height: 200)
}
}
Button("Scroll to 3/4 of the content height via proxy") {
guard let scrollViewProxy = scrollViewProxy else { return }
let contentOffset = CGPoint(x: 0, y: min(scrollViewProxy.contentSize.height * 3.0 / 4, scrollViewProxy.maxContentOffset.y))
scrollViewProxy.setContentOffset(contentOffset, animated: true, completion: { completed in
print("Scrolled via proxy to \(contentOffset)! Completed: \(completed)")
})
}
}
.onPreferenceChange(ContainedScrollViewKey.self) {
scrollViewProxy = $0
}
}
}
For paging, SwiftUI has a different hidden type called _PagingView
. It’s a wrapper on scroll view that supports paging.
The direction of the scrolling and the size of the page in the scrolling direction is controlled by _PagingViewConfig
.
All pages take the same size. If size is nil
, page takes the full available width or height on the screen (in correspondance with the scroll direction).
It’s useful for implementation of custom tab bar, image carousel or other pagination behavior.
The usage example is shown below.
import SolidScroll
struct PagingContentView: View {
@State var currentPage: Int = 0
let config: PagingViewConfig
init() {
var config = PagingViewConfig()
config.direction = .horizontal
config.size = 100
config.margin = 0
config.spacing = 0
self.config = config
}
var body: some View {
PagingView(config: config, page: $currentPage, views: Array(0...10).map { page(at: $0) })
}
@ViewBuilder
func page(at index: Int) -> some View {
Text("Page \(index)")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(backgroundColor(at: index))
}
func backgroundColor(at index: Int) -> Color {
let indexMod = (index % 3)
switch indexMod {
case 0: return Color.red
case 1: return Color.green
case 2: return Color.blue
default: return Color.clear
}
}
}
If you want to use SolidScroll in any other project that uses SwiftPM, add the package as a dependency in Package.swift
:
dependencies: [
.package(name: "SolidScroll", url: "https://github.com/edudnyk/SolidScroll.git", from: "0.0.1"),
]
Next, add SolidScroll as a dependency of your test target:
targets: [
.target(name: "MyApp", dependencies: ["SolidScroll"], path: "Sources"),
]
If you use Carthage, you can add the following dependency to your Cartfile:
github "edudnyk/SolidScroll" ~> 0.0.1
If your project uses CocoaPods, add the pod to any applicable targets in your Podfile:
target 'MyApp' do
pod 'SolidScroll', '~> 0.0.1'
end