ScrollViewReader's scrollTo may be broken on iOS 15

ScrollViewReader's scrollTo scrolls too much in iOS 15. I had no problem with iOS 14.

This is the code:

import SwiftUI

struct ContentView: View {
  var body: some View {
    ScrollViewReader { proxy in
      ScrollView {
        VStack {
          Color.yellow
            .frame(height: 800)

          ScrollButton(proxy: proxy)
        }
      }
    }
  }
}

struct ScrollButton: View {
  let proxy: ScrollViewProxy

  @Namespace var bottomId

  var body: some View {
    VStack {
      Button("Scroll") {
        withAnimation {
          proxy.scrollTo(bottomId)
        }
      }
      Color.red
        .frame(height: 500)
        .id(bottomId)
    }
  }
}

struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView()
  }
}

I am not sure if this is sort of a bug or not, but you can send a bug report when you find some code once was working does not run in the latest beta.

I'm experiencing the same problem. No issues on the iOS14 device, however, scrolls too far on the iOS15 device.

I have the same issue but in horizontal direction

import SwiftUI



struct ContentView: View {

  var body: some View {
      ScrollViewReader { proxy in
          ScrollView {
              VStack {
                  Color.yellow
                      .frame(height: 900)

                  ScrollButton(proxy: proxy)

                  Color.yellow
                      .frame(height: 900)
              }
              .padding(.horizontal, 40)
          }
      }
  }
}



struct ScrollButton: View {

  let proxy: ScrollViewProxy
  @Namespace var bottomId

    var body: some View {
        VStack {
            Button("Scroll") {
                withAnimation {
                    proxy.scrollTo(bottomId, anchor: .top)                    
                }
            }
        }.id(bottomId)
    }

}



struct ContentView_Previews: PreviewProvider {

  static var previews: some View {
    ContentView()
  }

}

+1

+1

I have the same problem. The code was running perfectly, now it overscrolls. I'll submit a bug report. Hopefully Apple will fix this issue before release.

It seems the issue is related to the .padding(.horizontal, 40) on the VStack within the ScrollView. If that padding is applied instead to the views within the VStack, the issue is resolved (although I definitely still think this is a bug).

+1 on this issue.

The workaround from @brian_bern does work (thank you for this Brian), but can only be used in certain scenarios. For instance, you have to add padding to the enclosed H/VStack when you're trying to prevent the ScrollView from clipping shadows, a spring animation on the scaleEffect when the button isPressed, or anything else that could draw outside the ScrollView (more on ScrollView clipping: https://developer.apple.com/forums/thread/653827).

Does anyone know if there is a place where one can see existing SwiftUI Bug Reports and their status? Or should I just submit my own bug report and follow that? It doesn't look like this was mentioned in the Known Issues of the iOS 15 release notes and I'd love to follow something to confirm if/when Apple acknowledges this as a confirmed issue as well as if/when they plan on a fix.

I sent feedback to Apple by the bug report a month ago, but I haven't received a reply yet. It has also been reproduced in the official release version of iOS 15.

I have the same problem, please let us know if some of you have the answer or solution, cheers

+1

Confirmed this has not been fixed in iOS 15.0.1

+1 as well

In my case, LazyVStack helped to fix the scrollTo issue


struct ContentView: View {
 var body: some View {
  ScrollViewReader { proxy in
   ScrollView {
    VStack {
     Color.yellow
      .frame(height: 800)

     ScrollButton(proxy: proxy)
    }
   }
  }
 }
}

struct ScrollButton: View {
 let proxy: ScrollViewProxy

 @Namespace var bottomId

 var body: some View {
  LazyVStack {
   Button("Scroll") {
    withAnimation {
     proxy.scrollTo(bottomId)
    }
   }
   Color.red
    .frame(height: 500)
    .id(bottomId)
  }
 }
}

struct ContentView_Previews: PreviewProvider {
 static var previews: some View {
  ContentView()
 }
}

+1. We are also having this issue. The above workarounds didn't fix it for us either.

Can anyone confirm if this was fixed when compiling with XCode 13.1?

Same problem with us.

+1

Adding that I'm also experiencing this problem on MacOS Monterey 12.0.1 as well as iOS 15

Found a workaround that works for me on both OSX and iOS. I suspected the bug was happening from scrollTo() incorrectly calculating the offset of the particular view in the reference frame of the scrollview, as I was getting different scroll positions depending on how far down the list the scrollTo view was.

Previously the scrollview content was composed of different container views, with subviews inside those. The workaround was to flatten those container views so the requested view IDs to scrollTo are all flat at the top level of the scroll view content.

ScrollView setup:

ScrollViewReader { proxy in
        GeometryReader { geometry in
            ScrollView(showsIndicators: false) {
                VStack(alignment: .leading, spacing: 17) {
                    ForEach(levelSelect.worlds) { world in
                        flatRowItems(world: world)
                    }
                }
            }
            .onAppear {
                if let id = levelSelect.selectedLevelID {
                    proxy.scrollTo(id, anchor: .center)
                }
            }
            .onChange(of: levelSelect.selectedLevelID) { selectedLevelID in
                guard let selectedLevelID = selectedLevelID else { return }
                if !levelSelect.isInitialSelection {
                    withAnimation {
                        proxy.scrollTo(selectedLevelID, anchor: nil)
                    }
                } else {
                    levelSelect.isInitialSelection = false
                    proxy.scrollTo(selectedLevelID, anchor: .center)
                }
            }
        }
    }

Flattened scrollview content using @ViewBuilder:

@ViewBuilder func flatRowItems(world: World) -> some View {
        Group {    // previously had a WorldRow which contained the following content as child views
            if world.locked {
                LockedWorldRow(world: world)
            } else {
                UnlockedWorldRow(world: world)    // previously UnlockedWorldRow contained LevelRows inside

                ForEach(world.levels) { level in
                    LevelRow(levelItem: level)
                    .id(level.id)
                }
            }
        }
    }

Flattening the hierarchy will definitely help. Although for a big feature like ours it's an impossible task. We decided to scroll to a larger parent view instead. Really hoping that Apple fixes this bug soon.

Has mentionned by @archy88 for me replacing inner VStack with LazyVStack did fix the issue.

Expect that will help some of you.

     
  ScrollViewReader { scrollProxy in
     
    ScrollView(.vertical) {
       
      VStack (spacing: 0) {
         
        ForEach(parents) { parent in
           
          Text(parent.name)
            .padding()
           
          LazyVStack (spacing: 0) { // With VStack Scroll issue
               
            ForEach(parent.childs) { child in
              
              ChildView(child)
                .id(child.uuid)
                .padding()
            }
          }
          .id(parent.id)
        }
      }
       
      Spacer()
    }
    .onAppear() {
      if let selectedChild = selectedChild {
        scrollProxy.scrollTo(selectedChild.uuid, anchor: .center)
      }
    }
  }
  .padding()
}

Just to mention it, we observed some problems on iOS 15 and employed the workaround with attaching the view IDs to the views at top level. However, immediately after that we realized that it makes sense even from UX perspective: considering e.g. the case of scrollable section with title, it makes sense to scroll to the section as whole rather to the title of the section, as it makes difference when there's not enough content below the section to scroll it to the top: in that scenario, attaching the id to the section makes the whole section rather just the title scrolled into in the view.

From my testing, this appears to be fixed in iOS 15.4 developer beta. My radar reproduction works perfectly on the beta and was broken on 15.3 and earlier

ScrollViewReader's scrollTo may be broken on iOS 15
 
 
Q