ScrollViewProxy的scrollTo()在列表滚动时失效,如何解决?
解决SwiftUI列表减速滚动时scrollTo()失效的问题
当List处于减速滚动状态时,调用scrollTo()方法不会立即生效,必须等滚动完全停止后才能触发,这个问题在iOS 17真机上必现,模拟器偶现。要解决这个问题,我们可以借助UIKit的UIScrollView API强制停止滚动,再执行滚动到顶部的操作。
具体实现步骤
- 实现一个UIViewRepresentable组件,用来获取List底层的UIScrollView并控制其滚动状态
- 在视图中添加状态变量,触发停止滚动的操作
- 点击按钮时,先停止滚动,再调用
scrollTo()
修改后的完整代码
import SwiftUI struct Rows: View { @State private var stopScrolling = false var dataRows: [RowData] = [] init() { for i in 0...1000 { dataRows.append(RowData(id: i)) } } var body: some View { ScrollViewReader { scrollView in ZStack(alignment: .bottomTrailing) { List(dataRows) { row in NavigationLink { } label: { Row(data: row) } } .background( ScrollViewStopper(stopScrolling: $stopScrolling) ) Button("Scroll To Top") { print("button pressed") // 先强制停止滚动 stopScrolling = true // 延迟调用scrollTo,确保滚动已停止 DispatchQueue.main.async { scrollView.scrollTo(0) } } .buttonStyle(.bordered) } } } } struct Row: View { var data: RowData var body: some View { Text(String(data.id)) } } struct RowData: Identifiable { var id: Int } // 用来控制UIScrollView滚动状态的组件 struct ScrollViewStopper: UIViewRepresentable { @Binding var stopScrolling: Bool func makeUIView(context: Context) -> UIView { UIView() } func updateUIView(_ uiView: UIView, context: Context) { guard stopScrolling else { return } // 找到List对应的UIScrollView if let scrollView = uiView.superview?.superview as? UIScrollView { // 强制停止滚动 scrollView.setContentOffset(scrollView.contentOffset, animated: false) } // 重置状态,避免重复触发 stopScrolling = false } }
代码说明
ScrollViewStopper通过UIViewRepresentable嵌入到List的背景中,利用视图更新机制监听stopScrolling状态变化- 当
stopScrolling变为true时,找到底层的UIScrollView,调用setContentOffset(_:animated:)并传入当前偏移量、关闭动画,以此强制停止滚动 - 点击按钮时,先设置
stopScrolling = true触发停止操作,再通过DispatchQueue.main.async确保滚动停止后调用scrollTo(),保证操作顺序正确
这样修改后,无论列表是否处于减速滚动状态,点击按钮都能立即停止滚动并跳转到顶部。
内容的提问来源于stack exchange,提问作者Ben Robinson
相关产品推荐
相关产品推荐

