如何在SwiftUI中向兄弟视图传递多个ScrollViewProxy以分别控制Table的滚动
如何在SwiftUI中向兄弟视图传递多个ScrollViewProxy以分别控制Table的滚动
这个问题我之前也碰到过,SwiftUI的作用域规则确实经常给这种跨视图的交互添堵——要么proxy被锁在闭包里拿不出来,要么多个可滚动视图抢同一个proxy的控制权。别担心,我们可以用状态变量来托管这些proxy,轻松解决作用域的问题。
核心思路
我们需要把每个ScrollViewProxy从它的闭包作用域里"带出来",存到父视图的状态中,这样兄弟视图(也就是你的按钮)就能直接访问到对应的proxy了。最简洁的方式是用@State变量来保存两个proxy实例,不需要额外的ObservableObject(当然用也可以,看个人习惯)。
完整代码示例
import SwiftUI struct TableScrollDemo: View { // 用@State存储两个ScrollViewProxy,初始为nil @State private var firstTableProxy: ScrollViewProxy? @State private var secondTableProxy: ScrollViewProxy? var body: some View { HStack(spacing: 20) { // 第一个Table的ScrollViewReader ScrollViewReader { proxy in Table((0..<100).map(IdentifiableInt.init)) { TableColumn("", value: \.id.description) } .frame(width: 150) // 在视图出现时,把当前proxy赋值给状态变量 .onAppear { firstTableProxy = proxy } } // 第二个Table的ScrollViewReader ScrollViewReader { proxy in Table((0..<100).map(IdentifiableInt.init)) { TableColumn("", value: \.id.description) } .frame(width: 150) .onAppear { secondTableProxy = proxy } } // 控制按钮区域 VStack(spacing: 20) { Button("滚动第一个Table到99") { // 调用第一个proxy的scrollTo方法,可选绑定避免空值崩溃 firstTableProxy?.scrollTo(99) } Button("滚动第二个Table到99") { secondTableProxy?.scrollTo(99) } } } .padding() } } // 你的IdentifiableInt保持不变 struct IdentifiableInt: Identifiable { let id: Int } #Preview { TableScrollDemo() }
关键细节解释
- 状态变量托管Proxy:用
@State声明两个可选的ScrollViewProxy变量,它们的生命周期和父视图一致,能安全地存储proxy实例。 - 传递Proxy到状态:在每个
ScrollViewReader的闭包里,通过.onAppear把当前的proxy赋值给对应的状态变量——这一步确保proxy已经被正确初始化,不会拿到空值。 - 按钮调用滚动:在按钮的action闭包里,直接通过状态变量调用
scrollTo方法,因为状态变量是父视图的属性,所有子视图都能访问到。 - 可选绑定的安全性:用
?.调用scrollTo,避免因为proxy未初始化(比如视图还没出现)导致的崩溃。
为什么之前的方法不行?
- 单独给每个Table包
ScrollViewReader:proxy的作用域只限于闭包内部,外部的按钮根本看不到它。 - 给两个Table包同一个
ScrollViewReader:ScrollViewReader会自动绑定到第一个可滚动的视图(也就是你的第一个Table),所以不管怎么调用,都会只滚动第一个Table。
这个方案在任何支持ScrollViewReader的macOS版本上都能正常工作,完全符合你的需求~
内容来源于stack exchange
相关产品推荐
相关产品推荐

