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.

Cómo mover sincrónicamente el último elemento en un ScrollView con la aparición del teclado?

Estoy implementando una interfaz de mensajería para mi aplicación. Sin embargo, cuando hago clic en TextField para mostrar el teclado, ScrollView no mantiene el último mensaje en la parte inferior del límite de ScrollView. En otras palabras, el teclado “cubrirá” algunos de los mensajes más recientes, tendría que desplazarme hacia arriba manualmente para ver los últimos mensajes (ver video en el siguiente enlace: video).

He intentado utilizar ScrollViewReader para desplazarme hacia el último mensaje una vez que TextField está enfocado, pero tendría que utilizar una función asincrónica DispatchQueue con un retraso, lo que significa que los mensajes se desplazarán a la posición correcta después de que el teclado se haya movido a su lugar.

Esto es bastante poco natural en comparación con iMessage, Twitter y básicamente todas las demás aplicaciones que tienen funciones de mensajería.

¿Cómo puedo resolver este problema?

Tags:  ,

Answer

  1. Avatar for davy.ai

    Una solución a este problema es implementar una KeyboardObservingScrollView personalizada que observe los cambios en la altura del teclado y ajuste el margen interno y el desplazamiento del ScrollView. Esto asegurará que el último mensaje siempre sea visible por encima del teclado.

    Pasos para implementar la KeyboardObservingScrollView:

    1. Crea un ScrollView personalizado que tenga una propiedad @ObservedObject para observar los cambios en la altura del teclado.
    struct KeyboardObservingScrollView<content: view="">: View {
        @ObservedObject private var keyboard = KeyboardResponder()
        let content: Content
    
        init(@ViewBuilder content: () -> Content) {
            self.content = content()
        }
    
        var body: some View {
            ScrollView {
                VStack(alignment: .leading, spacing: 10) {
                    content
                }
                .padding(.bottom, keyboard.height)
                .background(
                    GeometryReader { proxy in
                        Color.clear.onAppear {
                            self.keyboard.width = proxy.size.width
                        }
                    }
                )
            }
        }
    }
    
    1. Crea una clase KeyboardResponder que observe los cambios en la altura del teclado y notifique a la KeyboardObservingScrollView.
    class KeyboardResponder: ObservableObject {
        @Published var height: CGFloat = 0
        var width: CGFloat = 0
        private var cancellable: AnyCancellable?
    
        init() {
            let keyboardWillShow = NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)
            let keyboardWillHide = NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)
    
            cancellable = Publishers.Merge(keyboardWillShow, keyboardWillHide)
                .compactMap {
                    $0.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect
                }
                .map {
                    $0.height > 0 ? $0.height - UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0 : 0
                }
                .receive(on: DispatchQueue.main)
                .assign(to: \.height, on: self)
        }
    }
    
    1. Utiliza la KeyboardObservingScrollView en tu vista de mensajería y establece el foco del ScrollViewReader en la parte inferior para asegurarte de que el último mensaje siempre sea visible.
    struct MessagingView: View {
        @State private var messages = ["¡Hola!", "¿Cómo estás?", "¡Estoy bien, gracias!"]
    
        var body: some View {
            VStack {
                KeyboardObservingScrollView {
                    ScrollViewReader { proxy in
                        LazyVStack(alignment: .leading, spacing: 10) {
                            ForEach(messages, id: \.self) { message in
                                Text(message)
                                    .padding(10)
                                    .foregroundColor(.white)
                                    .background(Color.blue)
                                    .cornerRadius(10)
                            }
                        }
                        .padding()
                        .onChange(of: messages.count) { _ in
                            withAnimation {
                                proxy.scrollTo(messages.count - 1, anchor: .bottom)
                            }
                        }
                    }
                }
                .background(Color(UIColor.systemGray6))
                .cornerRadius(10)
                .padding()
    
                TextField("Escribe un mensaje...", text: .constant(""))
                    .padding(10)
                    .background(Color(UIColor.systemGray6))
                    .cornerRadius(10)
                    .padding()
            }
        }
    }
    

    Con esta implementación, la KeyboardObservingScrollView ajustará el margen interno y el desplazamiento del contenido para asegurarse de que el último mensaje siempre sea visible por encima del teclado.

Comments are closed.