GO FURTHER, FASTER: Try the Swift Career Accelerator today! >>

SOLVED: How to determine which List row that we've scrolled to is in view?

Forums > SwiftUI

@beely  

Hi guys,

Newbie here, working on my first Swift and SwiftUI Mac app, and Paul's resources here have been an outrageously helpful and well put-together resource.

It's been going great so far, but I'm having a problem with List scrolling and could do with some guidance from more experienced people.

I have a List (wrapped in a ScrollViewReader) that for demonstration purposes can be thought of as groups containing a Title item (which has an ID) and a number of text lines following it:-

Title (0)

  • some item
  • some item

Title (1)

  • some item ...

etc. As I use my cursor up/down keys, the list will scroll to the prev/next Title item so the Title is at the top of the view (using ScrollTo with the Title ID).

This works great, until you scroll the list using the scroll bar, as now we don't know what Title is in view.

Any ideas, strategies or pointers as to how I can determine the topmost Title item in view that I've scrolled to, so that when I resume using the cursor keys, I can keep the position and scroll to the next or previous one from my current scroll position?

Now I'm off to read up on GeometryReader... wish me luck! ;)

Cheers!

1      

@beely  

Ok, so on the basis that when I've finally exhausted all my investigation and resorted to making a post asking for help, that's usually when I figure something out. :)

I've managed to implement this using the following component: https://github.com/Ceylo/ListItemTracking

It's an iOS package, but I was able to use it in my Mac project directly. You basically attach the new onItemFrameChanged modifier to the items you want to track - in my case, the "Title" Text items, and as you scroll it returns either a frame of co-ordinates if the item is in view, or nil if it's not in view.

struct ContentView: View {
    var body: some View {
        GeometryReader { geometry in
            List(0..<100) { i in
                Text("Item \(i)")
                    .onItemFrameChanged(listGeometry: geometry) { (frame: CGRect?) in
                        print("rect of item \(i): \(String(describing: frame)))")
                    }
            }
            .trackListFrame()
        }
    }
}

By saving the visibility state back to my data as the List is scrolled, it just takes a bit of logic to inspect the visible items, and their position, and then decide on which item the prev/next cursor keys will scroll to from that point.

There's a few edge cases I've got to handle - it seems if you scroll the list very fast things may not update as you like, so I might need to implement a few extra things, but in any case, it's working pretty well!

2      

Hacking with Swift is sponsored by RevenueCat.

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure and A/B test your entire paywall UI without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.