-2

I tried the same code with an Int instead of messages, it worked.

When I try and use the message object, for some reason it's not liking it. Even though the .id is UUID().

The messages appear in scrollView and can be scrolled manually although it's not scrolling automatically to the bottom once a message is added.

I've tried .self instead of .id in the ForEach, flipping the order of ScrollView and ScrollViewReader, not using a .id on MessageRow, downcast to UUID inside scrollTo, nothing seems to be working...

What am I missing?

Thank you in advance!

             ScrollViewReader { scrollView in
                ScrollView(.vertical) {
                    LazyVStack {
                        ForEach(groupViewModel.messages) { message in
                            MessageRowView(message: message)
                                .padding(.leading)
                        }
                    }
                    .onAppear {
                        if !groupViewModel.messages.isEmpty {
                            if let last = groupViewModel.messages.last {
                                scrollView.scrollTo(last)
                            }
                        }
                    }
                }
            }
struct Message: Codable, Equatable, Identifiable, Hashable {
    let id: UUID
    let sender: FSUser
    let message: String
    let sendDate: Date
}
Osydon
  • 3
  • 2

1 Answers1

0

Welcome to Stack Overflow! You are missing the simple fact that a message.id != message?.id. Your .id(message.id) is a non-optional UUID, however, groupViewModel.messages.last?.id is an optional. Since they don't match, you won't get the scroll. I will give you one better. First, you don't need to use message.id. You can simply compare the messages themselves as Identifiable conforms to Equatable. So, your code becomes:

        ScrollViewReader { scrollView in
            ScrollView(.vertical) {
                LazyVStack {
                    // You can also skip id: \.id as message is Identifiable
                    ForEach(groupViewModel.messages) { message in
                        MessageRowView(message: message)
                            .id(message)
                    }
                }
                .onAppear {
                    DispatchQueue.main.asyncAfter(deadline: .now()) {
                        if !groupViewModel.messages.isEmpty {
                            // Use if let to unwrap the optional
                            if let last = groupViewModel.messages.last {
                                // Use the unwrapped value here
                                scrollView.scrollTo(last)
                            }
                        }
                    }
                }
            }
        }

If you haven't stated conformance of your struct to Identifiable, just do it. I am assuming you did, but you did not post that part of the code so we don't have a Minimal, Reproducible Example (MRE). Most questions will need an MRE to be answered, so make sure you understand what that is.

Edit:

In looking at your struct, try this:

struct Message: Codable, Equatable, Identifiable, Hashable {
    let id = UUID()
    let sender: FSUser
    let message: String
    let sendDate: Date
}

You can set the UUID immediately as it will never change. Also, not that it matters in the long run, but if you conform to Identifiable you get 'Equatable' conformance.

Yrb
  • 8,103
  • 2
  • 14
  • 44
  • It's still not working...I updated the optional (thanks for the tip) and added the Message struct to the question for reference (MRE). I've updated the latest code as well which includes your tip regarding dismissing the .id given Message already conforms to identifiable. Any other ideas that might activate the scroll?! thanks – Osydon Mar 21 '22 at 20:17
  • Do you still have `.id(message.id)` in your code? If so, remove it. – Yrb Mar 21 '22 at 20:19
  • No, I updated the code in the question to the latest one that's still not working. Does FSUser (custom object) and every custom object under it have to conform to identifiable as well? – Osydon Mar 21 '22 at 20:24
  • I edited my answer to put a an `id()` back on as well as used a `DispatchQueue.main.asyncAfter(deadline:)` to help with timing issues. – Yrb Mar 21 '22 at 20:32
  • I appreciate your suggestions because I've already learned* what I didn't know although it is still not working despite adding id and dispatchQueue and conformance to identifiable. – Osydon Mar 21 '22 at 20:40
  • Then you will need to post a [Minimal Reproducible Example (MRE)](https://stackoverflow.com/help/minimal-reproducible-example), as I just tested the code I posted. It was working fine. You may need to add some time in the `DispatchQueue`. The problem with using `.onAppear()` is it is called before the view is actually on screen. You can't use `.scrollTo()` before the view is on screen, so you have a timing issue. Try adding `+2' after `.now()` and wait 2 seconds for it to scroll. – Yrb Mar 21 '22 at 20:45
  • Bingo! I replaced onAppear with onChange and it worked. THANK YOU. I needed every bit of your help. You might want to update your response to onChange. No need for the DispatchQueue either. – Osydon Mar 21 '22 at 20:59