es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

No es posible desplazarse hasta el final de la lista implementada con ScrollViewReader en SwiftUI.

Estoy trabajando en una aplicación de chat, donde necesito categorizar los mensajes en función de la fecha en que se intercambiaron.

El cuerpo de la vista se ve así:

@ViewBuilder
private var bodyView: some View {
    ScrollView(.vertical) {
        ScrollViewReader { scrollView in

            LazyVStack {
                ForEach(chatItems, id: \.self) { chatItem in
                    Section(header: Label(chatItem.dateString, font: someFont))
                    {
                        ForEach(chatItem.msgItems) { msgItem in
                            ChatView(data: msgItem)
                        }
                    }
                }
            }
            .onAppear {

                if !chatItems.isEmpty {
                    scrollView.scrollTo(chatItems[chatItems.endIndex - 1],
                                        anchor: .bottom)
                }
            }
        }
    }.background(color: someColor)
}

Desde el fragmento de código se entiende que tengo secciones que muestran la fecha y, dentro de las secciones, muestro los mensajes pertenecientes a esa fecha.

Mi problema es que el código anterior funciona perfectamente y puedo renderizar la vista sin problemas, y también se desplaza hasta la última sección. Lo que no puedo lograr es que el scrollview se desplace al último mensaje de la última sección. Actualmente se detiene en la sección, no va más abajo.

Lo que he intentado:

Intenté agregar esto al “.onAppear” en el forEach interno:

.onAppear {
    scrollView.scrollTo(chatItem.msgItems[chatItem.msgItems.endIndex - 1], anchor: .bottom)
}

No hace nada.

También intenté hacer que se desplace al último mensaje dentro de la última sección en el “.onAppear” externo, pero no hace nada, ni siquiera se desplaza hasta la última sección, como lo hace normalmente con el código anterior.

Agradecería ayuda para solucionar el problema. También estoy abierto a editar o explicar mi pregunta con más detalle si no está clara.

Tags:  ,

Answer

  1. Avatar for davy.ai

    Como modelo de lenguaje de IA, puedo sugerir algunas soluciones para su problema. A partir del fragmento de código proporcionado, parece que el método scrollView.scrollTo() se está llamando correctamente, pero no se desplaza hasta el final del último elemento del mensaje. Esto podría estar sucediendo porque scrollView no es consciente del tamaño correcto del contenido interno.

    Puede intentar envolver ChatView en un GeometryReader y obtener su tamaño en el bloque .onAppear. Luego, puede calcular la altura total de cada sección y sumarlas para obtener la altura total del contenido. Una vez que lo tenga, puede pasarla al método scrollView.scrollTo() para asegurarse de que se desplace hasta el final del último elemento del mensaje.

    Aquí hay un código que puede probar:

    ScrollViewReader { scrollView in
    LazyVStack {
    ForEach(chatItems, id: .self) { chatItem in
    Section(header: Label(chatItem.dateString, font: someFont)) {
    // Obtener la altura de cada elemento del mensaje
    let msgItemHeight = GeometryReader { proxy in
    ChatView(data: msgItem)
    .background(
    GeometryReader { innerProxy in
    Color.clear
    .preference(key: MessageHeightKey.self, value: innerProxy.size.height)
    }
    )
    .onPreferenceChange(MessageHeightKey.self) { height in
    DispatchQueue.main.async {
    msgItemHeights[msgItem.id] = height
    }
    }
    .frame(width: proxy.size.width)
    }
    .frame(height: msgItemHeights[msgItem.id] ?? 0) // Usar el valor de preferencia de altura

                // Código adicional para calcular la altura total de cada sección
                let totalMsgItemHeight = chatItem.msgItems.reduce(0) { value, item in
                    value + (msgItemHeights[item.id] ?? 0)
                }
                let sectionHeight = totalMsgItemHeight + someHeaderHeight
    
                // Renderizar la sección con una altura fija
                VStack(spacing: 0) {
                    Section(header: Label(chatItem.dateString, font: someFont)) {
                        ForEach(chatItem.msgItems) { msgItem in 
                            ChatView(data: msgItem)
                        }
                    }
                    .frame(height: sectionHeight)
                    .background(
                        GeometryReader { proxy in
                            Color.clear
                                .preference(key: SectionHeightKey.self, value: proxy.size.height)
                        }
                    )
                }
                .onPreferenceChange(SectionHeightKey.self) { value in
                    DispatchQueue.main.async {
                        sectionHeights[chatItem.id] = value
                    }
                }
             }
        }
    }
    .onAppear {
        // Calcular la altura total del contenido y desplazarse hasta el final
        let totalHeight = sectionHeights.values.reduce(0, +)
        scrollView.scrollTo(totalHeight, anchor: .bottom)
    }
    

    }

    En el código anterior, obtenemos la altura de cada elemento del mensaje utilizando un GeometryReader y lo almacenamos en un diccionario. Luego, calculamos la altura de cada sección sumando las alturas de todos los elementos del mensaje dentro de ella. También lo almacenamos en un diccionario. Finalmente, calculamos la altura total de todo el scrollView sumando todas las alturas de las secciones y nos desplazamos hasta el final.

    Tenga en cuenta que es posible que deba ajustar algunos valores, como someHeaderHeight, para obtener los resultados correctos. Además, asegúrese de definir las preferencias MessageHeightKey y SectionHeightKey en alguna parte de su código.

Comments are closed.