I added a background view to my SwiftUI List, and would like to move it up as user scrolls (similar to the effect of that of the Health app). I can't add it onto the background of a List row, because List unconditionally clips content to a row, and I would like the view to extend past a insetted row's bounds (scrollClipDisabled
does not work on List). So I added the view as the background view of the entire List.
Currently, I am achieving this by monitoring contentOffset using the new onScrollGeometryChange(for:of:action:)
modifier, updating a state variable that controls the offset of the background view. The code looks something like this:
struct ContentView: View {
@State private var backgroundOffset: CGFloat = 0
var body: some View {
List {
...
}
.background {
backgroundView
.offset(y: backgroundOffset)
}
.onScrollGeometryChange(for: ScrollGeometry.self) { geometry in
geometry
} action: { oldValue, newValue in
let contentOffsetY = newValue.contentOffset.y
let contentInsetY = newValue.contentInsets.top
if contentOffsetY <= -contentInsetY {
backgroundOffset = 0
} else {
backgroundOffset = -(contentOffsetY + contentInsetY)
}
}
}
}
However, this results in bad scrolling performance. I am guessing this is due to backgroundOffset
being updated too frequently, and thus refreshing the views too often? If so, what is a more performant approach to achieve the desired effect? Thanks!
What is a performant way to change view offset as user scrolls?
You'd basically shift the location of a view’s content using offset(x:y:) modifier and update it whenever a user scrolls the view beyond the top of its content. It's a similar logic to what you currently have:
ScrollView {
// ...
}
.onScrollGeometryChange(for: Bool.self) { geometry in
geometry.contentOffset.y < geometry.contentInsets.top
} action: { wasBeyondZero, isBeyondZero in
self.isBeyondZero = isBeyondZero
}
can't add it onto the background of a List row, because List unconditionally clips its content, and I would like the view to extend past the inset grouped list's bounds. So I added the view as the background view of the entire List.
Have you tried using a plain list instead .listStyle(.plain)