I have horizontal progress bar within ScrollView and I need to change that progress bar value, when user is scrolling.
Is there any way to bind some value to current scroll position?
You can do this with a few GeometryReader
s.
My method:
ScrollView
containerPreferenceKey
s to set the proportion
@State
valueCode:
struct ContentView: View {
@State private var scrollViewHeight: CGFloat = 0
@State private var proportion: CGFloat = 0
var body: some View {
VStack {
ScrollView {
VStack {
ForEach(0 ..< 100) { index in
Text("Item: \(index + 1)")
}
}
.frame(maxWidth: .infinity)
.background(
GeometryReader { geo in
let scrollLength = geo.size.height - scrollViewHeight
let rawProportion = -geo.frame(in: .named("scroll")).minY / scrollLength
let proportion = min(max(rawProportion, 0), 1)
Color.clear
.preference(
key: ScrollProportion.self,
value: proportion
)
.onPreferenceChange(ScrollProportion.self) { proportion in
self.proportion = proportion
}
}
)
}
.background(
GeometryReader { geo in
Color.clear.onAppear {
scrollViewHeight = geo.size.height
}
}
)
.coordinateSpace(name: "scroll")
ProgressView(value: proportion, total: 1)
.padding(.horizontal)
}
}
}
struct ScrollProportion: PreferenceKey {
static let defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
Result: