0

Whenever a message is received from the backend, the app should automatically scroll to the beginning of the message. I cannot find a way for this to work. I've tried the stackoverflow solutions here, here and here to no avail.

Here's my code

struct ContentView: View {
    @State private var inputText = ""
    @State private var messages: [Message] = []
    @State private var showThumbsDownSheet = false
    @State private var feedbackText = ""
    @State private var showThanksForFeedback = false

    
    var body: some View {
        VStack {
            NavigationView {
                ScrollViewReader { scrollView in
                    List {
                        ForEach(messages, id: \.id) { message in
                            
                            Text(message.text)
                            if !message.isFromUser && message.isReplyFromBackEnd {
                                HStack {
                                    ThumbsUpButton(message: message)
                                    Spacer()
                                    ThumbsDownButton(message: message, showThumbsDownSheet: $showThumbsDownSheet, reviewText: $feedbackText)
                                }
                            }
                        }
                    }
                    .onChange(of: messages) { value in
                        scrollView.scrollTo(self.messages.count - 1)
                    }
                }
            .navigationTitle("Ask Tais")
            .toolbar {
                ToolbarItemGroup(placement: .navigationBarTrailing) {
                    Menu {
                        Button(action: { exit(0) }) {
                            Label("Quit app", systemImage: "")
                        }
                    }
                    label: {
                        Label("Add", systemImage: "ellipsis.circle")
                    }
                }
            }
        }
            .onAppear {
                                // Add a "Hello" message to the list of messages when the app is first opened
                self.messages.append(Message(id: UUID(), text: "Hello!", isFromUser: false, isReplyFromBackEnd: false, responseId: "0"))
                            }
            .frame(maxHeight: .infinity)
            HStack {
                TextField("What do you want to say?", text: $inputText)
                Button(action: {
                    self.messages.append(Message(id: UUID(), text: self.inputText, isFromUser: true, isReplyFromBackEnd: false, responseId:"0"))
                    sendRequest(with: self.inputText) { responseText, id in
                    self.messages.append(Message(id: UUID(), text: responseText, isFromUser: false, isReplyFromBackEnd: true, responseId: id))
                    }
                    self.inputText = ""
                }) {
                    Text("Ask Tais")
                }
            }
            .padding()
        }
    }
}
wachichornia
  • 1,178
  • 14
  • 31
  • You need to scroll to an `id`, not an index – jnpdx Jan 09 '23 at 17:06
  • just changed to scrollView.scrollTo(messages.last!.id) and it still does not work – wachichornia Jan 09 '23 at 17:22
  • `messages` won't be set yet. `onChange` fires before it's set. So, it would be `value.last!.id`. Also, since you have multiple children for each `ForEach` element, I'm not exactly sure if that will work. You may want to group the contents in `Group { }` or something – jnpdx Jan 09 '23 at 18:04
  • You may also need to group it in `Task { @MainActor in ... }` – jnpdx Jan 09 '23 at 18:04

1 Answers1

0

ScrollViewReader and scrollTo does not work on List with changing content. You have to use ScrollView instead of List and build your own list design.

ChrisR
  • 9,523
  • 1
  • 8
  • 26