How to pass multiple ScrollViewProxy's to a sibling view?

How to pass multiple ScrollViewProxy's to a sibling view?
typescript
Ethan Jackson

I have two Tables in an HStack. Also in this HStack is a view with some buttons that will programmatically scroll each table when clicked.

Here is a toy example with the tables displaying some numbers.

HStack { Table((0..<100).map(IdentifiableInt.init)) { TableColumn("", value: \.id.description) } Table((0..<100).map(IdentifiableInt.init)) { TableColumn("", value: \.id.description) } VStack { Button("Scroll First Table") { // this should scroll the first table to id 99 } Button("Scroll Second Table") { // this should scroll the second table to id 99 } } }
struct IdentifiableInt: Identifiable { let id: Int }

To programmatically scroll the tables, I need two ScrollViewReaders. If I wrap each table with their own ScrollViewReader, I can no longer access the ScrollViewProxy in the buttons' action closures.

HStack { ScrollViewReader { proxy1 in Table((0..<100).map(IdentifiableInt.init)) { TableColumn("", value: \.id.description) } } ScrollViewReader { proxy2 in Table((0..<100).map(IdentifiableInt.init)) { TableColumn("", value: \.id.description) } } VStack { Button("Scroll First Table") { proxy1.scrollTo(99) // 'proxy1' is out of scope here :( } Button("Scroll Second Table") { proxy2.scrollTo(99) // 'proxy2' is out of scope here :( } } }

If I wrap both tables in one ScrollViewReader, then both buttons end up scrolling the first table only.

ScrollViewReader { proxy in HStack { Table((0..<100).map(IdentifiableInt.init)) { TableColumn("", value: \.id.description) } Table((0..<100).map(IdentifiableInt.init)) { TableColumn("", value: \.id.description) } VStack { Button("Scroll First Table") { proxy.scrollTo(99) // scrolls the first table } Button("Scroll Second Table") { proxy.scrollTo(99) // also scrolls the first table :( } } } }

I have also tried scrolling with ScrollPosition and .scrollPosition. While this does work if I were scrolling a ScrollView, it does not scroll the Tables at all.

How can I access the ScrollViewProxys for each table in the VStack?

I don't have a minimum deployment target. Solutions can require any macOS version.

Answer

Creating extra @States to store the ScrollViewProxys and assigning them in onAppear seems to work, but the @States need to be optional, which in my opinion is a bit ugly.

@State private var proxy1: ScrollViewProxy? @State private var proxy2: ScrollViewProxy? var body: some View { HStack { ScrollViewReader { proxy in Table((0..<100).map(IdentifiableInt.init)) { TableColumn("", value: \.id.description) } .onAppear { proxy1 = proxy } } ScrollViewReader { proxy in Table((0..<100).map(IdentifiableInt.init)) { TableColumn("", value: \.id.description) } .onAppear { proxy2 = proxy } } VStack { Button("Scroll First Table") { proxy1?.scrollTo(99) } Button("Scroll Second Table") { proxy2?.scrollTo(99) } } } }

Related Articles