简体   繁体   中英

Horizontal ScrollView bounces vertically in SwiftUI on change

I'm having a problem with the ScrollView in iOS 15. When using scrollTo the items bouncing vertically.

I didn't have that problem in iOS 14. The bounce is very random no logic at all for trying to understand when it will jump.

If I'm removing the padding from the scroll view it's fixed, but I need that extra space as requested by the UI designer.

Also, tried to use .frame instead of .padding and same results.

Does anyone know how to fix this problem or maybe why it happens only in iOS 15?

滚动视图错误

Code:

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)
              }
            }
         }
    }
}
}

The problem is the vertical padding on the HStack .

Minimal reproducible example of the problem

Here is the problem in minimal code, which anyone can run. Use this code as a reference to see what changes:

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)
    }
}

Solution

You can remove the vertical padding from the HStack by just doing .horizontal padding, then add the .vertical padding to each Text view instead.

Code:

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
前 后

Use .bottom instead of .center in proxy.scrollTo(index, anchor: ...) .


Vertical content offset is changing during proxy.scrollTo animation if we set .top or .center anchor. It's look like some apple bug. To avoid that we should align to such anchor that results to zero offset during scrolling.

For horizontal ScrollView we should change:

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

For vertical:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM