SwiftUI控件之 ScrollView(二)

实现快速滚动到顶部或底部的效果

在很多的ScrollView 的应用中,我们经常见到点击“回到顶部”按钮实现快速回到顶部的效果。

在 SwiftUI中,如果我们想要实现这样的效果,我们可以使用ScrollViewRaderScrollView来实现。ScrollViewRader可以让我们通过编程的方式实现滚动到一个已知的子视图的位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

struct ContentView: View {
@Namespace var topID
@Namespace var bottomID

var body: some View {
ScrollViewReader { proxy in
ScrollView {
// 顶部的按钮
Button {
withAnimation {
proxy.scrollTo(bottomID)
}
} label: {
Text("滚动到底部")
}
.id(topID)

// 滚动视图
VStack(spacing: 0.1) {
ForEach(0..<100) {i in
color(fraction: Double(i) / 100)
.frame(height: 32)
}
}
// 顶部的按钮
Button(action: {
withAnimation {
proxy.scrollTo(topID)
}
}, label: {
Text("滚动到顶部")

})
.id(bottomID)
}
}
}

// 创建颜色
func color(fraction: Double) -> Color {
Color(red: fraction, green: 1 - fraction, blue: 0.5)
}
}

在上面的示例代码中,实现了一下几个步骤:

  1. ScrollView外面嵌套了一层ScrollViewReader
  2. ScrollView的子视图包括顶部按钮、 100 个渐变颜色子视图和底部按钮是三个部分;
  3. 使用@NameSpace创建了topIDbottomID 两个变量,然后将它们分别给到顶部和底部按钮;
  4. 利用ScrollViewRader 提供的proxy 在点击两个按钮的时候滚动到指定的 ID 的子视图位置。

效果如下:

实现滚动到指定索引的位置

创建一个数据结构:

1
2
3
4
5
struct NameModel: Identifiable {
let name:String
let id: String = UUID().uuidString
let index: Int
}

生成一些测试使用的数据源:

1
2
3
4
5
6
7
8
// 数据源
var nameDatas:[NameModel] {
get {
(0..<100).map {
NameModel(name: "我是-\($0)", index: $0)
}
}
}

界面布局:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var body: some View {
ScrollViewReader { proxy in
ScrollView {
Button(action: {
withAnimation {
proxy.scrollTo(nameDatas[scrollToIndex].index)
}
}, label: {
Text("滚动到索引为\(scrollToIndex)的地方")
})
ForEach(nameDatas) { data in
Text(data.name)
.padding(4)
.id(data.index)
}

}
}
}

此时的效果为当我们点击按钮后,ScrollView将滚动到子视图 ID为 70 的位置:

注意:这里目前依然存在一个问题,当我们使用数据模型的id属性作为ScrollView子视图的ID 时,即 .id(data.id),无法实现类似的效果。