Heshan
Heshan

Reputation: 985

SwiftUI ScrollViewReader scrollTo doesn't scroll all the way to top

I want the ScrollView to scroll to top when I tap on the tab. It works but it stops parallel to the navigation title and doesn't scroll all the way below the title as expected.

The current behaviour is,

1st tap on tab: scrolls parallel to the navigation title (Image 1)

Tap again: scrolls below nav title as expected (Image 2)

This is a poc of a much larger code so using a ListView is not an option.

1st Tap 2nd Tap

struct Tabs {
    static let One = "1"
    static let Two = "2"
    static let Three = "3"
}

struct ContentView: View {
  @State var scrollToTop = false
  @State private var currentTab: String = Tabs.One
  var body: some View {
      TabView(selection: tabSelection) {
        TestView(scrollToTop: $scrollToTop)
          .tabItem {
            Label("First", systemImage: "1.circle")
          }.tag(Tabs.One)
      }
  }

  var tabSelection: Binding<String> {
    Binding( get: {
      currentTab
    }, set: { newValue in
      /// Tapped on the same tab.
      if newValue  == currentTab {
        switch newValue {
        case Tabs.One:
          scrollToTop = true
        case Tabs.Two:
          print(newValue)
        case Tabs.Three:
          print(newValue)
        default:
          return
        }
      }
      currentTab = newValue
    })
  }
}

struct TestView: View {
  @Binding var scrollToTop: Bool
  var body: some View {
    NavigationView {
      ScrollView {
        ScrollViewReader { proxy in
          LazyVStack {
            Text("Text 0")
              .id(0)
            

            // Simulating other views (these views will be added manually in the real app without the ForEach)
            ForEach(1...50, id: \.self) { index in
              Text("Text \(index)")
            }
          }
          .navigationTitle("Title")
          .onChange(of: scrollToTop) {
            if scrollToTop {
              withAnimation { // works without withAnimation only if scroll is not ongoing.
                proxy.scrollTo(0)
                // proxy.scrollTo(0, anchor: .bottom) doesn't work with any anchor as well
              }
              scrollToTop = false
            }
          }
        }
      }
    }
  }
}

tried setting anchor of scrollTo, not working

tried without withAnimation. This works (although it's ugly) if scrolling is not ongoing. If you tap the tab while scroll is still happening the behaviour is even stranger.

Upvotes: 1

Views: 244

Answers (1)

blackhatsrak
blackhatsrak

Reputation: 185

The id you wrote in Foreach and the id of Text("Text 0") have the same ids, so you can change one of these ids and fix it according to which one you want to be unique.

 Text("Text 0")
     .id(0)                          
     // Simulating other views (these views will be added manually in the real app without the ForEach)
    ForEach(0...50, id: \.self) { index in
    Text("Text \(index)")
     }

Upvotes: 0

Related Questions