SwiftUI – Simple ScrollViewReader use to get ScrollView to start at the top left.

Had occasion to use a SwiftUI ScrollView today and ran into the issue where the SwiftUI team decided to use one of the two most sensible defaults for initial presentation when the content is too large to fit in the container – center the content. This makes sense for images or maps, but not for much of anything else I am thinking of. Everything else seems to want top left (for left-to-right locales at least). Strange that something that a large percentage of people are going to want isn’t as easy to set as alignment or something, but so it goes.

Anyway, this gave me an opportunity to read up on ScrollViewReader which, combined with the .onAppear modifier, can get the initial presentation of a ScrollView to the top left like so many want/need it to be.

Putting the code here so I remember for next time and in case anyone else finds a bazillion Stack Overflow posts about how ScrollView is so hard to make display properly but no modern ScrollViewReader answers.

//
//  ContentView.swift
//  trashme
//

import SwiftUI

struct ContentView : View {
  var cellSize: CGFloat = 50
  var numRows: Int = 30
  var numCols: Int = 10
  var body : some View {

    VStack(alignment: .leading, spacing: 20) {
      ScrollViewReader() { proxy in
        ScrollView([.horizontal,.vertical], showsIndicators: true) {
          HStack( alignment: .top, spacing: 0) {
            VStack ( alignment: .leading, spacing: 0) {
              ForEach(0 ..< numRows, id: \.self) { row in
                Text("row " + row.description)
                  .frame( height: self.cellSize )
              }
            }
            ForEach(0 ..< self.numCols, id: \.self) { col in
              VStack( spacing: 0) {
                ForEach(0 ..< self.numRows, id: \.self) { row in
                  Text("\(row), \(col)")
                    .frame( width: self.cellSize, 
                            height: self.cellSize, 
                            alignment: .center)
                    .border(.blue)
                }
              }
            }
          }
          .id("root")  // since our ForEach views don't really 
          		// have true ids (\.self ~)  
          		// we make one for the outer HStack
          		// in the ScrollView
        }
        .onAppear {
          proxy.scrollTo("root", anchor: .topLeading)
        }
      }
    }
  }
}

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

And here’s what it looks like in an iOS SwiftUI preview pane:

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: