Reputation: 391
So I'm try to use ScrollViewReader to programmatically scroll a horizontal scroll view. I thought it would work like scrollToItem with .centeredHorizontally in UIKit, and for the most part it does, but the last few elements in the scroll view are being forcefully scrolled to the center of the screen, despite the fact that the scroll view isn't normally able to scroll that far over (without snapping back after releasing the drag, at least). This ends up creating white space across the trailing half of the screen.
I've seen some other questions about this and it seems like the general opinion is that it's not a bug? On the one hand I suppose we're telling the scroll view to center the item, and it's doing just that -- so, not a bug? On the other hand, that's not how similar functionality worked in UIKit. Also, this behavior is only happening on the trailing end of the scroll view! If it was the intended behavior I would expect that scrolling to the first element in the scroll view using .center anchor would force it into the center of the screen and leave leading white space, but this doesn't happen.
Is there an elegant solution to this? Or do we have to calculate the width of our elements + spacing and figure out based on the screen width whether we should anchor .center or just scroll to the last element with anchor .trailing in order to replicate the UIKit behavior?
Upvotes: 13
Views: 3653
Reputation: 693
If you have any paddings set to any view inside your ScrollView
, make sure you set both sides for symmetry, do NOT set only 1 side.
ScrollViewReader { proxy in
ScrollView() {
VStack() {
// your content here...
}
// NO! because this will cause the ScrollViewReader to mess
// with your VStack's y-offset
.padding([.leading], yourPaddingSize)
// YES! Set both sides
.padding([.leading, .trailing], yourPaddingSize)
}
}
Upvotes: 0
Reputation: 808
I experienced the same issue when using scrollTo
with a non-nil anchor
such as .center
for content that has padding
in a ScrollView
, and was able to find a workaround:
I wanted my ScrollView
to contain a VStack
containing several elements, where I could scroll to each element using ScrollViewReader
. However, when I applied horizontal padding
to the VStack
, I experienced the above bug.
When I applied horizontal padding to the individual elements of the VStack
instead of to the VStack
itself, the bug seems to have resolved itself.
Now, when using scrollTo("someId")
, the scroll position does not have a wonky horizontal offset equal to the padding of the ScrollView
's content!
Upvotes: 1
Reputation: 11
I can confirm this behavior and think it should be considered a bug. Especially since the scroll view will "jump" into position on the first touch event.
As of iOS 15.4 Beta 1 this is fixed for me. Maybe give it another try.
Upvotes: 1
Reputation: 391
I found a package (Amzd/ScrollViewProxy) that was made before ScrollViewReader was released that functions much the same as ScrollViewReader, but also seems to not have the bug (if it is a bug) detailed in the question.
Usage examples can be seen on the repository page, but here's a quick minimal example.
ScrollView(.horizontal) { scrollProxy in
ForEach(sections) { section in
Text(section.text)
.scrollId(section.id)
}
.onChange(of: index) {
scrollProxy.scrollTo(
sections[index].id,
alignment: .center
)
}
}
The package adds a convenience init to ScrollView to give access to the scrollProxy.
Upvotes: 0