水平 ScrollView 在 SwiftUI 中发生变化时垂直弹跳

Horizontal ScrollView bounces vertically in SwiftUI on change

我在 iOS 15 中遇到 ScrollView 的问题。 使用 scrollTo 时,项目会垂直弹跳。

我在 iOS 14 中没有遇到过这个问题。反弹非常随机,没有任何逻辑可言,无法理解它何时会跳跃。

如果我从滚动视图中删除填充,它是固定的,但我需要额外的 space UI 设计者的要求。

此外,尝试使用 .frame 而不是 .padding,结果相同。

有谁知道如何解决这个问题或者为什么它只在 iOS 15 中发生?

代码:

ScrollView(.horizontal, showsIndicators: false) {
    
    ScrollViewReader{ proxy in
        HStack(spacing: 32){
            ForEach(...){ index in
                QuestionCell(...)
                    .scaleEffect(selectedIndex == index ? 1.175 : 1.0)
                    .onTapGesture{
                        withAnimation(.spring()){
                            
                            selectedIndex = index
                        }
                    }
            }
            
        }
        .padding(.leading)
        .padding() // Removing this fixes the bounce bug.
        .onChange(of: selectedIndex) { value in
        
            withAnimation(.spring()){
              let paramsCount = <SOME MODEL>.count
            
              if value < paramsCount{
                  proxy.scrollTo(value, anchor: .center)
              }else{
                  proxy.scrollTo(paramsCount - 1, anchor: .center)
              }
            }
         }
    }
}
}

问题是 HStack 上的垂直填充。

问题的最小可重现示例

这是最小代码的问题,任何人都可以运行。使用此代码作为参考,看看有什么变化:

struct ContentView: View {
    @State private var selectedIndex = 0

    var body: some View {
        ScrollView(.horizontal, showsIndicators: false) {
            ScrollViewReader { proxy in
                HStack(spacing: 32) {
                    ForEach(0 ..< 10, id: \.self) { index in
                        Text("Question cell at index: \(index)")
                            .background(Color(UIColor.systemBackground))
                            .scaleEffect(selectedIndex == index ? 1.175 : 1.0)
                            .onTapGesture {
                                withAnimation(.spring()) {
                                    selectedIndex = index
                                    proxy.scrollTo(index, anchor: .center)
                                }
                            }
                    }
                }
                .padding(.leading)
                .padding() // Removing this fixes the bounce bug
            }
        }
        .background(Color.red)
    }
}

解决方案

您可以通过 .horizontal 填充从 HStack 移除垂直填充,然后将 .vertical 填充添加到每个 Text 视图。

代码:

struct ContentView: View {
    @State private var selectedIndex = 0

    var body: some View {
        ScrollView(.horizontal, showsIndicators: false) {
            ScrollViewReader { proxy in
                HStack(spacing: 32) {
                    ForEach(0 ..< 10, id: \.self) { index in
                        Text("Question cell at index: \(index)")
                            .background(Color(UIColor.systemBackground))
                            .scaleEffect(selectedIndex == index ? 1.175 : 1.0)
                            .onTapGesture {
                                withAnimation(.spring()) {
                                    selectedIndex = index
                                    proxy.scrollTo(index, anchor: .center)
                                }
                            }
                            .padding(.vertical) // <- HERE
                    }
                }
                .padding(.leading)
                .padding(.horizontal) // <- HERE
            }
        }
        .background(Color.red)
    }
}
Before After

proxy.scrollTo(index, anchor: ...) 中使用 .bottom 而不是 .center


如果我们设置 .top 或 .center 锚点,proxy.scrollTo 动画期间垂直内容偏移会发生变化。它看起来像一些苹果虫。 为避免这种情况,我们应该对齐在滚动过程中导致零偏移的锚点。

对于水平滚动视图,我们应该更改:

  • .trailing -> .bottomTrailing
  • .center -> .bottom
  • .leading -> .bottomLeading

对于垂直:

  • .top -> .topTrailing
  • .center -> .trailing
  • .bottom -> .bottomTrailing