I am trying to write my own MyScrollView
to manage scroll. I am doing this because I want to have a LazyVStack
with items that have varying heights and for which I can scroll to any item inside. So, this is what I began with:
ScrollView {
LazyVStack {
ForEach(itemsWithVaryingHeights, id: .key) {
ViewWithVariableHeight($0)
}.scrollTargetLayout()
}
}
Evidently, it is impossible to programmatically reliably scroll to a particular item since SwiftUI does not know how big my views are and gets confused which offset to scroll to.
Fine. I can calculate the height of my own SectionView
s and manage my own scroll. So I thought to create my own barebones version of ScrollView:
struct MyScrollView<Child> : View where Child : View {
@State var offsetY: CGFloat = 0
public var child: Child
init(@ViewBuilder child: () -> Child) {
self.child = child()
}
var body: some View {
child
.offset(y: offsetY)
.gesture(
DragGesture()
.onChanged { value in
offsetY = value.translation.height
}
.onEnded { value in
let nextScrollPosition = value.predictedEndTranslation.height
withAnimation(.spring()) {
offsetY = nextScrollPosition
}
}
)
}
}
Then use it like so:
MyScrollView {
LazyVStack {
ForEach(itemsWithVaryingHeights, id: .key) {
ViewWithVariableHeight($0)
}
}
}
This does not work as I expect since the entire LazyVStack is rendered, essentially not being lazy anymore. I am hoping I could get an understanding why that is. I thought that SwiftUI’s ScrollView
didn’t know anything about how lazy or not the child view is. But in this case, it looks like it does handle lazy views differently somehow?
How can I replicate what ScrollView
does? I am also all ears if someone can suggest a better approach to solve my problem.