How to get and set SwiftUI List scroll position?

Just getting started with SwiftUI. Exciting stuff.


I understand that List would be one of the main building blocks that I can use in place of UI/NSTableView. One thing I haven’t seen yet: how to get and set the scroll position? Specifically, my list would consist of “read” and “unread” items. When the user first enters the list, everything is unread, so the list can start at the top. As new items scroll into view, they become “read”. When the user now leaves the list, and later returns, the list should scroll to the first “unread” item. So I am not so much interested in the pixel-level scroll offset, but rather, how do I scroll the viewport into the right location so that the user would see the desired info. The API could be something like “scroll the list such that model object X is visible in the middle” (with or without animation).


The read/unread state is easily expressible as a model object property. I’m having trouble with how to bind it to the list scrolling in this new declarative world of ours.

Accepted Reply

Update for iOS 14 and macOS Big Sur (i.e this year’s update of SwiftUI).

If using a ScrollView instead of list, there is new API for setting the scroll position: https://developer.apple.com/documentation/swiftui/scrollviewproxy/scrollto(_:anchor:)

I discussed this with Apple engineers during a SwiftUI lab. No clean API to get scroll position to implement something like infinite load. But at least we can set it cleanly now. For many cases of displaying content, using ScrollView + LazyVStack is better than using a List, and then we can also use this new API.

Replies

I agree with this need.


Even going through the tutorials and making a dynamic list where the user can add new objects, the list doesn't automatically scroll to the new row, nor can I figure out how to programatically scroll to the new row.

I’m trying to dynamically set a header based on the row in the list the user is currently at. Is there anything similar to willDisplay from UITableViews? I’m not sure how this would all work in a declarative UI.

I have this need as well. Did anyone ever figure out how to scroll to a particular row in the list when the view first appears?

I need this too. Please post if you have a solve. Also no good way to have alternate color for rows in a list.

No answer in 6 months? This would help me too! How can we set the first visible row on a SwiftUI List?

Haven’t heard anything about it anywhere, sadly.

Unfortunately there appears to be no way to do this in SwiftUI at present. Somewhat annoying, since when the library was in its pupal stages there was a very-much-public-and-settable contentOffset property on ScrollView. I can only guess that there were some very nasty edge cases that couldn't be worked out at the time.


So far the only way I know of obtaining the scroll offset is to place an invisible/empty view on top of the List (i.e. in a ZStack), and another at the top of the List/ScrollView's content, and then using some anchors and preferences to obtain and publish the global coordinates of each. The distance between the second hidden view (which will move upwards as the scroll view content moves) and the first view (which stays in its location on screen) will give you the scroll offset. Unfortunately, there's nothing you can do to put that information back into the scrolling view at all, though you can use it to implement pull-to-refresh; see https://swiftui-lab.com/scrollview-pull-to-refresh/

Does not seem possible today (in fact List still have a lot of limits).


Did you see this for workaround, with na scrollView or tableView of yours:

https://stackoverflow.com/questions/57121782/scroll-swiftui-list-to-new-selection

Harsha -


I know this is a bit late ...
If you do something like:

List(arrayData, id: \.self) { index in

MakeRow(..., isEven: index.isMultiple(of: 2))

}

That will let you set different colors on odd and even rows.

Update for iOS 14 and macOS Big Sur (i.e this year’s update of SwiftUI).

If using a ScrollView instead of list, there is new API for setting the scroll position: https://developer.apple.com/documentation/swiftui/scrollviewproxy/scrollto(_:anchor:)

I discussed this with Apple engineers during a SwiftUI lab. No clean API to get scroll position to implement something like infinite load. But at least we can set it cleanly now. For many cases of displaying content, using ScrollView + LazyVStack is better than using a List, and then we can also use this new API.

So in 4 years, still don’t have functionality of a basic NS/UITableView. Seems there is time to add massive new features but not fix the basic one here. Have a NavigationSplitView with a List, and keyboard commands to advance the list when the list is collapsed or not. When not collapsed the selection happily scrolls off screen which is not acceptable. Arrow keys properly scroll the list to the selection, but that is built-into List.

Looks like I have to rewrite to a (limited to 10 item VStack, ScrollView, and ScrollViewProxy) as a workaround. But my list is a flattened list of files. How about a List.scrollToSelection() call? No Apple samples of a real basic UI using SwiftUI, just recipe books with fixed count elements.