Home > Software engineering >  Horizontal ScrollView bounces vertically in SwiftUI on change
Horizontal ScrollView bounces vertically in SwiftUI on change

Time:09-26

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?

ScrollView bug

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

CodePudding user response:

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
Before After
  • Related