水平 ScrollView 在 SwiftUI 中在更改时垂直反弹

Posted

技术标签:

【中文标题】水平 ScrollView 在 SwiftUI 中在更改时垂直反弹【英文标题】:Horizontal ScrollView bounces vertically in SwiftUI on change 【发布时间】:2021-09-23 11:59:10 【问题描述】:

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

我在 iOS 14 中没有这个问题。弹跳是非常随机的,根本没有逻辑来试图了解它何时会跳动。

如果我从滚动视图中删除填充,它是固定的,但我需要 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)
              
            
         
    


【问题讨论】:

【参考方案1】:

问题是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

【讨论】:

【参考方案2】:

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


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

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

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

对于垂直:

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

【讨论】:

以上是关于水平 ScrollView 在 SwiftUI 中在更改时垂直反弹的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI:NavigationLink 内的水平 ScrollView 会中断导航

SwiftUI 水平 ScrollView 具有不可见的填充

在 SwiftUI ScrollView 中停止垂直滚动

SwiftUI ScrollView 垂直和水平滚动

SwiftUI:调整 ScrollView 的边界

如何在 SwiftUI 中制作水平列表?