我在 SwiftUI 中实现了以下清理器。+
按钮应该ScrollView
向上移动 1 个刻度(或scrollPosition
增加 1),但问题是,直到我单击 8-9 次后才会发生滚动。这是 iOS 中的错误还是编程错误?
struct BrokenVerticalScrubberDemo: View {
@State private var scrollPosition: Int? = 0
@State private var count: Int = 20
var body: some View {
VStack {
Text("Scroll Position: \(scrollPosition ?? 0)")
.font(.headline)
ScrollView(.vertical, showsIndicators: true) {
VStack(spacing: 8) {
ForEach(0..<count, id: \.self) { index in
Text("Tick \(index)")
.frame(height: 30)
.id(index)
}
}
.scrollTargetLayout()
.padding(.vertical, 50)
}
.scrollTargetBehavior(.viewAligned)
.scrollPosition(id: $scrollPosition)
.frame(height: 300)
.border(Color.gray)
Button("+1") {
withAnimation {
scrollPosition = min((scrollPosition ?? 0) + 1, count - 1)
}
}
.padding(.top)
}
.padding()
}
}
#Preview {
BrokenVerticalScrubberDemo()
}
相反,如果我使用ScrollViewReader
解决方法,它会在点击 2 次“+”按钮后开始滚动。
import SwiftUI
struct SomeWhatWorkingVerticalScrubberDemo: View {
@State private var scrollPosition: Int = 0
@State private var count: Int = 20
var body: some View {
VStack {
Text("Scroll Position: \(scrollPosition)")
.font(.headline)
ScrollView(.vertical, showsIndicators: true) {
ScrollViewReader { scrollViewProxy in
VStack(spacing: 8) {
ForEach(0..<count, id: \.self) { index in
Text("Tick \(index)")
.frame(height: 30)
.id(index)
}
}
.padding(.vertical, 50)
.onChange(of: scrollPosition) { newPosition in
withAnimation {
scrollViewProxy.scrollTo(newPosition, anchor: .center)
}
}
}
}
.frame(height: 300)
.border(Color.gray)
Button("+1") {
scrollPosition = min(scrollPosition + 1, count - 1)
}
.padding(.top)
}
.padding()
}
}
#Preview {
SomeWhatWorkingVerticalScrubberDemo()
}
请尝试使用顶部锚点
在滚动位置上。
关于默认锚点行为的文档对我来说有点晦涩难懂。但在您的示例中,它似乎是底部锚点。
添加锚点可能会修复按钮滚动问题,但并不能真正修复洗涤器的布局和整体使用中的缺陷,因为您永远无法选择整个值范围(例如 0 或 13 以上的任何值)。
此外,如果拖动洗涤器(不使用按钮),值将无法正确对齐 - 它们要么一次前进两个值,要么需要拖动两个步骤才能将其增加一步,如下面的记录所示。
还有一个问题,设置 的默认值
scrollPosition
实际上并不是这样工作的。在您的示例中,您将其设置为零,并且在 中展开时将其显示为零Text
,但实际上它仍然是nil
。因此,如果您需要通过将默认值设置为 5 来实际滚动到第 5 个刻度
scrollPosition
,则它不起作用。要真正使其工作,请保留无默认值并在 中设置初始.onAppear
值ScrollView
。视图对齐(如果正确实现)应该适用于任何锚点(甚至没有锚点)。在这种情况下,除 之外的任何锚点
.top
基本上都不起作用(甚至 .top 也存在上述问题)。VStack
造成这一切的罪魁祸首是用于布局目标的垂直填充:以下是重现该问题的完整代码,其中包含选择不同锚点的选项:
解决方案:
修复方法相当简单,只需移除 中的任何填充
VStack
,并向 增加边距ScrollView
,考虑到每个 的高度tick
,以便垂直居中所有内容并允许选择完整的值范围。因此,在这种情况下,它将是:以下是完整的工作代码:
请注意,上面的代码中没有使用锚点,因为在这个用例中它不是真正需要的,其中刻度标记位于滚动视图的中心。
回顾一下:
使用根据滚动视图尺寸和单个视图大小调整填充
.contentMargins
。ScrollView
.onAppear
设置滚动视图的默认滚动位置值。