功能
ScrollViewReader 不是滚动容器,而是 只负责"滚动导航" 的视图包装器。它生成一个 proxy,供内部代码调用 scrollTo(id, anchor:) 将任意子项瞬间或动画地滚动到可见区域。
核心 API
swift
proxy.scrollTo<ID>(
id, // 与子项 .id 同类型
anchor: .top // 可选,目标对齐位置
)
- 不返回任何值;若 id 不存在静默忽略。
- 必须包在
withAnimation内才能产生平滑滚动。
示例
swift
struct ScrollViewReaderBootcamp: View {
@State var textFieldText: String = ""
@State var scrollToIndex: Int = 0
var body: some View {
VStack {
TextField("Enter a # here..", text: $textFieldText)
.frame(height: 56)
.border(.gray)
.padding()
.keyboardType(.numberPad)
Button("SCROLL NOW") {
withAnimation(.spring()) {
if let index = Int(textFieldText) {
scrollToIndex = index
}
// anchor 就是目标元素最终的位置
// proxy.scrollTo(30, anchor: .top)
}
}
ScrollView {
ScrollViewReader { proxy in
ForEach(0..<50) { index in
Text("This is item \(index)")
.font(.headline)
.frame(height: 180)
.frame(maxWidth: .infinity)
.background(.white)
.cornerRadius(8)
.shadow(radius: 8)
.padding()
.id(index) // proxy.scrollTo 需要配合 .id() 使用
}
.onChange(of: scrollToIndex) { oldValue, newValue in
withAnimation(.spring()) {
proxy.scrollTo(newValue, anchor: .top)
}
}
}
}
}
}
}
与 ScrollView 的关系
- ScrollViewReader 自身不滚动、不占位,仅提供控制句柄。
- 一个 Reader 可包裹多个 ScrollView / List;每个 ScrollView 内部子项 id 唯一即可。
- 支持横向滚动:使用
ScrollView(.horizontal)即可,API 不变。
注意事项
scrollTo必须在 Reader 的闭包内调用,否则编译错误。- 若 id 重复或缺失,滚动无效果且不报错,请保证数据范围正确。
- 键盘弹出、屏幕旋转等场景,可结合
GeometryReader动态计算锚点,避免遮挡。