0

I've tried several solutions that I've found but none work for me, mostly lengthy with compile errors that I didn't understand how to fix. I collaborated with an AI today and after the third iteration edited this compact solution that compiled - but the List still does not scroll automatically. I'm very, very new at Swift.

The backing Array of String is updated by a published and observableObject:

class Logger: ObservableObject {
    
    @Published var messageArray = ["Start by selecting the project's data folder."]
    
    func addMessage(_ msg: String) {
        messageArray.append("\(timeNow()) *  \(msg)")
    }

I create an instance in my main (and only) ContentView

@ObservedObject var logger = Logger()

The code below is in a VStack and updates but does not scroll when the messageArray is changed by some other code. It is for macOS not IOS

    List(logger.messageArray, id: \.self) {
        message in
        Text(message)
    }
    .padding()
    .background(ScrollViewReader { proxy in
        Color.clear.onAppear {
            proxy.scrollTo(logger.messageArray.last)
        }
        .onReceive(logger.$messageArray) { _ in
            withAnimation {
                proxy.scrollTo(logger.messageArray.last)
            }
        }
    })

I hope you can help me with this as I've spent way too much time on it.

Nate Lockwood
  • 3,325
  • 6
  • 28
  • 34
  • You shouldn’t be using ObservedObject where the object is owned — use StateObject instead. – jnpdx Apr 20 '23 at 21:08

1 Answers1

1

You could try this approach, to scroll to the last line of the list. It uses the important id and .onChange() and has the ScrollViewReader wrapping the List. Works for me.

class Logger: ObservableObject {
    
    @Published var messageArray = ["message"]
    
    func addMessage(_ msg: String) {
        messageArray.append("\(UUID().uuidString.prefix(5)) *  \(msg)") // <-- for testing
    }
    
}

struct ContentView: View {
    @StateObject var logger = Logger() // <-- here
    
    var body: some View {
        ScrollViewReader { proxy in
            List (logger.messageArray, id: \.self) { message in
                Text(message).id(message) // <-- here, need id
            }
            .onChange(of: logger.messageArray) { _ in    // <-- here
                withAnimation {
                    proxy.scrollTo(logger.messageArray.last)
                }
            }
            .padding()
        }
        Button("Add message") {
            logger.addMessage("test message")  // <-- for testing
        }
    }
    
}