ScrollViewReader scrollTo with .center anchor bug?
Asked Answered
M

4

13

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?

Mesoderm answered 25/7, 2021 at 2:13 Comment(0)
M
0

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.

Mesoderm answered 25/7, 2021 at 6:31 Comment(2)
Thanks for this. I thought I was losing it for a day until I saw this post. ScrollViewProxy is working as I'd expected the builtin reader to. I'm glad i sounds like this is fixed in 15.4+, I haven't tried.Shwa
Unfortunately, the bug still persists even in 15.6.1. Also the scrolling doesn't work properly when there is padding applied to the ScrollView. Gonna try this one since I'm really tired of trying do find workarounds for Apple's never-ending bugs in this component.Logy
L
1

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.

Linsang answered 31/1, 2022 at 10:38 Comment(1)
I have the same problem in iOS 16.4.Corneliacornelian
W
1

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!

Wounded answered 31/3, 2023 at 17:11 Comment(0)
M
0

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.

Mesoderm answered 25/7, 2021 at 6:31 Comment(2)
Thanks for this. I thought I was losing it for a day until I saw this post. ScrollViewProxy is working as I'd expected the builtin reader to. I'm glad i sounds like this is fixed in 15.4+, I haven't tried.Shwa
Unfortunately, the bug still persists even in 15.6.1. Also the scrolling doesn't work properly when there is padding applied to the ScrollView. Gonna try this one since I'm really tired of trying do find workarounds for Apple's never-ending bugs in this component.Logy
K
0

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)
    }
}
Kissiah answered 4/8, 2023 at 19:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.