Skip to content

Instantly share code, notes, and snippets.

@twof
Created April 1, 2024 05:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save twof/d7194a9de0f7c471f82bb6d13bd9b521 to your computer and use it in GitHub Desktop.
Save twof/d7194a9de0f7c471f82bb6d13bd9b521 to your computer and use it in GitHub Desktop.
import SwiftUI
import ComposableArchitecture
@Reducer
struct Scroll {
@ObservableState
struct State {
var scrollPosition: Double
var trackerId: String?
}
enum Action {
case scrollPosition(Double)
case setTracker(String?)
}
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case let .scrollPosition(position):
state.scrollPosition = position
return .run { send in
// Set the tracker id which causes the view to scroll,
// then unset it so it can be set again next time
await send(.setTracker(ScrollTracker.id))
await send(.setTracker(nil))
}
case let .setTracker(tracker):
state.trackerId = tracker
return .none
}
}
}
}
struct ContentView: View {
@Bindable var store = Store(
initialState: Scroll.State(scrollPosition: 0.0),
reducer: { Scroll() }
)
var body: some View {
Button("Down") {
store.send(.scrollPosition(store.scrollPosition + 30))
store.send(.setTracker(ScrollTracker.id))
}
ScrollView {
ZStack {
VStack {
ForEach(0..<100) { _ in
Rectangle()
.fill(Color.green.gradient)
.frame(height: 50)
}
}
ScrollTracker(offset: $store.scrollPosition.sending(\.scrollPosition))
}.scrollTargetLayout()
}
.scrollPosition(id: $store.trackerId.sending(\.setTracker))
}
}
struct ScrollTracker: View {
static let id = "tracker"
@Binding var offset: Double
var body: some View {
VStack {
Spacer(minLength: offset)
GeometryReader { geometry in
Rectangle()
.frame(height: 10)
.foregroundStyle(Color.red)
.id(ScrollTracker.id)
.task(id: geometry.frame(in: .scrollView(axis: .vertical))) {
let scrollGeometry = geometry.frame(in: .scrollView(axis: .vertical))
let verticalOffset = scrollGeometry.origin.y
print("scroll", scrollGeometry)
print("vertical offset", verticalOffset)
offset -= verticalOffset
offset = max(offset, 0)
print("offset", offset)
}
}
Spacer()
.layoutPriority(1)
}
}
}
#Preview {
ContentView()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment