swiftuiswiftui-scrollview

ScrollViewReader scrollTo with .center anchor bug?


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?


Solution

  • 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.