前言
WWDC 24 已经结束,我决定开始写一些关于 SwiftUI 框架即将推出的新特性的文章。今年,苹果继续填补空白,引入了对滚动位置更细粒度的控制。本周,我们将学习如何操作和读取滚动偏移。
使用 scrollPosition
SwiftUI 框架已经允许我们通过视图标识符跟踪和设置滚动视图的位置。这种方法效果不错,但不足以更准确地跟踪用户交互。
struct ContentView: View {
@State private var position: Int?
var body: some View {
ScrollView {
LazyVStack {
ForEach(0..<100) { index in
Text(verbatim: index.formatted())
.id(index)
}
}
.scrollTargetLayout()
}
.scrollPosition(id: $position)
}
}
在上面的代码示例中,我们使用了视图标识符和 scrollPosition 修饰符来跟踪和设置滚动视图的位置。虽然这种方法效果不错,但在某些情况下,尤其是需要更精确的用户交互跟踪时,它可能不够用。为了弥补这一不足,SwiftUI 引入了新的 ScrollPosition 类型,使我们能够通过偏移量、滚动视图的边缘、视图标识符等组合滚动位置。
新的 ScrollPosition 类型
SwiftUI 框架引入了新的 ScrollPosition 类型,使我们能够通过偏移量、滚动视图的边缘、视图标识符等组合滚动位置。
struct ContentView: View {
@State private var position = ScrollPosition(edge: .top)
var body: some View {
ScrollView {
Button("Scroll to bottom") {
position.scrollTo(edge: .bottom)
}
ForEach(1..<100) { index in
Text(verbatim: index.formatted())
.id(index)
}
Button("Scroll to top") {
position.scrollTo(edge: .top)
}
}
.scrollPosition($position)
}
}
如上例所示,我们定义了 position 状态属性,并使用 scrollPosition 视图修饰符将滚动视图与状态属性绑定。我们还放置了两个按钮,允许你快速滚动到滚动视图中的第一个或最后一个项目。ScrollPosition 类型提供了许多重载的 scrollTo 函数,使我们能够处理不同的情况。
为滚动添加动画
通过附加动画视图修饰符并传递 ScrollPosition 类型的实例作为 value 参数,我们可以轻松地为编程滚动添加动画。
struct ContentView: View {
@State private var position = ScrollPosition(edge: .top)
var body: some View {
ScrollView {
Button("Scroll to bottom") {
position.scrollTo(edge: .bottom)
}
ForEach(1..<100) { index in
Text(verbatim: index.formatted())
.id(index)
}
Button("Scroll to top") {
position.scrollTo(edge: .top)
}
}
.scrollPosition($position)
.animation(.default, value: position)
}
}
滚动到特定项目
我们添加了另一个按钮来将滚动视图的位置更改为随机项目。我们仍然使用 ScrollPosition 类型的 scrollTo 函数,但我们提供了一个可哈希的标识符。这个选项允许我们将位置更改为特定项目,通过使用 anchor 参数,我们可以选择所选视图的哪个点应该可见。
struct ContentView: View {
@State private var position = ScrollPosition(edge: .top)
var body: some View {
ScrollView {
Button("Scroll somewhere") {
let id = (1..<100).randomElement() ?? 0
position.scrollTo(id: id, anchor: .center)
}
ForEach(1..<100) { index in
Text(verbatim: index.formatted())
.id(index)
}
}
.scrollPosition($position)
.animation(.default, value: position)
}
}
滚动到特定偏移
最后但同样重要的是 scrollTo 函数的 point 参数重载,允许我们传递 CGPoint 实例以将视图滚动到内容的特定点。
struct ContentView: View {
@State private var position = ScrollPosition(edge: .top)
var body: some View {
ScrollView {
Button("Scroll to offset") {
position.scrollTo(point: CGPoint(x: 0, y: 100))
}
ForEach(1..<100) { index in
Text(verbatim: index.formatted())
.id(index)
}
}
.scrollPosition($position)
.animation(.default, value: position)
}
}
如上例所示,我们使用带有 CGPoint 参数的 scrollTo 函数。它还提供重载,允许我们仅按 X 或 Y 轴滚动视图。
struct ContentView: View {
@State private var position = ScrollPosition(edge: .top)
var body: some View {
ScrollView {
Button("Scroll to offset") {
position.scrollTo(y: 100)
position.scrollTo(x: 200)
}
ForEach(1..<100) { index in
Text(verbatim: index.formatted())
.id(index)
}
}
.scrollPosition($position)
.animation(.default, value: position)
}
}
读取滚动位置
我们学习了如何使用新的 ScrollPosition 类型操作滚动位置,这也允许我们读取滚动视图的位置。ScrollPosition 提供了可选的 edge、point 和 viewID 属性,以在你编程滚动时读取值。
每当用户与滚动视图交互时,这些属性将变为 nil。ScrollPosition 类型上的 isPositionedByUser 属性允许我们了解何时用户手势移动滚动视图内容。
提供一个可以运行示例:
下面是一个可以运行的示例代码,演示如何读取和显示滚动视图的位置。我们将使用一个 Text 视图来显示当前滚动位置:
import SwiftUI
struct ContentView: View {
@State private var position = ScrollPosition(edge: .top)
@State private var scrollOffset: CGPoint?
var body: some View {
VStack {
ScrollView {
LazyVStack {
ForEach(0..<100) { index in
Text("Item \(index)")
.id(index)
.padding()
.background(Color.yellow)
.cornerRadius(10)
.padding(.horizontal)
}
}
.scrollPosition($position)
.onScrollGeometryChange { geometry in
scrollOffset = geometry?.contentBounds.origin
}
}
.animation(.default, value: position)
if let offset = scrollOffset {
Text("Scroll Offset: x = \(Int(offset.x)), y = \(Int(offset.y))")
.padding()
} else {
Text("Scroll Offset: not available")
.padding()
}
}
.padding()
}
}
在这个示例中,我们使用了 onScrollGeometryChange 修饰符来读取滚动视图的几何变化。每当滚动视图滚动时,geometry?.contentBounds.origin 将提供当前滚动位置的偏移量。我们将这个偏移量存储在 scrollOffset 状态属性中,并在视图底部显示当前的滚动位置。
总结
在本文中,我们深入探讨了 SwiftUI 框架中 ScrollView 的新特性,特别是如何通过 ScrollPosition 类型实现更精确的滚动控制。我们介绍了如何使用 ScrollPosition 类型进行滚动位置的设置和读取,包括使用偏移量、视图标识符等方式进行操作。此外,我们还展示了如何通过动画和事件处理来增强用户体验。通过这些新功能,开发者可以更灵活地控制滚动视图的行为,从而创建更加流畅和直观的用户界面。希望这些内容对你有所帮助。