我正在ScrollView
SwiftUI 中构建一个水平对齐功能,将项目对齐到屏幕的中心。滚动时对齐功能运行良好,但视图首次加载时,初始项目会略微偏移,不会像预期的那样对齐到中心。
我发现问题出现在 的数量visibleItems
为偶数时。当 的数量为奇数时visibleItems
,初始对齐和捕捉从一开始就可以正常工作。
这是我的代码CircleScrollView
:
struct CircleScrollView: View {
@State(initialValue: 2)
var initialPosition: Int
@State(initialValue: 8)
private var visibleItems: Int
@State(initialValue: 0)
private var currentIndex: Int
private let spacing: CGFloat = 16
var body: some View {
ZStack(alignment: .leading) {
// For visuals of screen centre
Rectangle()
.fill(Color.gray.opacity(0.2))
.ignoresSafeArea()
.frame(maxWidth: UIScreen.main.bounds.width / 2, maxHeight: .infinity, alignment: .leading)
GeometryReader { geometry in
let totalSpacing = spacing * CGFloat(visibleItems - 1)
let circleSize = (geometry.size.width - totalSpacing) / CGFloat(visibleItems)
ScrollViewReader { scrollViewProxy in
ScrollView(.horizontal) {
HStack(spacing: spacing) {
ForEach(1..<100) { index in
ZStack {
Text("\(index)")
Circle().fill(Color(.tertiarySystemFill))
}
.frame(width: circleSize, height: circleSize)
.id(index)
}
}
.scrollTargetLayout()
.padding(.horizontal, (geometry.size.width - circleSize) / 2)
.onAppear {
scrollViewProxy.scrollTo(initialPosition, anchor: .center)
currentIndex = initialPosition
}
}
.scrollIndicators(.never)
.scrollTargetBehavior(.viewAligned)
}
}
}
}
}
问题:
visibleItems
当为偶数时,初始滚动位置会发生偏移。visibleItems
滚动或为奇数时可正常工作。
采取的步骤:
- 检查了和间距的计算
circleSize
。 - 通过调整填充来验证初始对齐。
问题:
- 当初始项目为偶数时,如何确保其正确对齐
visibleItems
? - 有没有更好的方法来处理初始加载期间的对齐或捕捉?
这个问题似乎与 上的水平填充有关
HStack
。它没有填充也能正常工作(但显然,只适用于不需要填充的位置)。我猜这
ViewAlignedScrollTargetBehavior
有点问题。作为一种解决方法,您可以尝试实现自己的ScrollTargetBehavior
。我尝试了一下,发现该函数的
updateTarget
调用方式不同,取决于它是第一次显示还是响应滚动手势:首次显示时,目标的锚点为
.center
,目标宽度为圆的宽度。目标原点的 x 偏移量包含屏幕宽度的一半。有趣的是,首次演出时无需修正。所以这似乎是哪里
ViewAlignedScrollTargetBehavior
出了问题。当随后调用滚动手势时,目标锚点为零,目标宽度是容器的宽度(
ScrollView
)。零锚点似乎被解释为
.topLeading
。因此,在这种情况下,目标的 x 偏移与 的前缘有关ScrollView
,而不是中心。我尝试更新目标锚点
.center
并调整目标宽度,但无法通过这种方法使其工作(它总是滚动太多)。最好保持目标锚点和宽度不变,并接受它与前缘相关。以下是特定于您的布局的自定义行为:
由于您使用状态变量来记录所选位置,因此将其用作 的
currentIndex
效果很好。这样,它会在滚动时更新,并且不需要。只需将变量更改为可选项即可使其工作。.scrollPosition
ScrollView
ScrollViewReader
以下是完全更新的示例,现在适用于偶数和奇数个可见项目: