我尝试在 SwiftUI 中复制 Snapchat 相机滤镜滚动。
当我手动滚动时,由于新的 iOS 17 滚动 API,它运行良好,它以每个圆圈为中心。但点击时它不起作用,scrollTo方法不保留圆的中心,我尝试了很多锚点但无法使其适用于所有圆。有时它适用于最后一个圆圈,但不适用于第一个圆圈。
就像你在下面看到的那样,在 gif 的开头,我正在滚动手势(工作),然后我点击(不再工作)。
如果我删除scrollTargetBehavior(.viewAligned)和scrollTargetLayout,则scrollTo正在工作。
这是重现该问题的简化代码:
import SwiftUI
@main
struct scrollApp: App {
var body: some Scene {
WindowGroup {
GeometryReader(content: { outerProxy in
ScrollViewReader(content: { proxy in
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(spacing: 0) {
ForEach(0...10, id: \.self) { index in
Circle()
.frame(width: 100, height: 100)
.foregroundStyle(.primary)
.id(index)
.onTapGesture {
print("tap \(index)")
withAnimation {
proxy.scrollTo(index, anchor: .center)
}
}
}
}
.padding(.horizontal, outerProxy.size.width * 0.5 - 100/2)
.scrollTargetLayout()
}
.scrollTargetBehavior(.viewAligned)
.overlay {
Circle()
.stroke(.red, lineWidth: 10)
.frame(width: 100, height: 100)
}
})
})
}
}
}
我已经尝试过你的代码,但有两个问题:
1. 您正在使用填充来
offset
修改内容以使其居中
2. 设置为 anchor
功能的 proxy.scrollTo
是 .center
,而根据我的测试,它必须是 .leading
。这可能就是它无法与 .scrollPosition
一起使用的原因。
所以,这就是解决方案:
@State private var activeIndex: Int?
var body: some View {
GeometryReader { outerProxy in
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(spacing: 0) {
ForEach(0...10, id: \.self) { index in
Circle()
.frame(width: 100, height: 100)
.foregroundStyle(.primary)
.id(index)
.onTapGesture {
print("tap \(index)")
withAnimation {
activeIndex = index
}
}
.offset(x: (outerProxy.size.width * 0.5 - 100/2))
}
}
/// Don't use padding. Use the offset on each individual element instead.
//.padding(.horizontal, outerProxy.size.width * 0.5 - 100/2)
.scrollTargetLayout()
}
.scrollTargetBehavior(.viewAligned)
.scrollPosition(id: $activeIndex, anchor: .leading)
.overlay {
Circle()
.stroke(.red, lineWidth: 10)
.frame(width: 100, height: 100)
}
}
}
在本例中我去掉了
ScrollViewReader
,因为由于新的 API,它是多余的。
您也可以使用旧的 API 来使用它。只需像我上面所做的那样删除应用于
LazyVStack
的填充,并使用应用于圆圈的偏移量,然后将 proxy.scrollTo
行更改为:
proxy.scrollTo(index, anchor: .leading)
让我知道这是否适合您!