I have two Table
s 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 ScrollViewReader
s. 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 Table
s at all.
How can I access the ScrollViewProxy
s for each table in the VStack
?
I don't have a minimum deployment target. Solutions can require any macOS version.
Answer
Creating extra @State
s to store the ScrollViewProxy
s and assigning them in onAppear
seems to work, but the @State
s 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)
}
}
}
}