¿Cómo funciona este ejemplo de enlace + estado de SwiftUI sin volver a invocar el cuerpo?

Aug 19 2020

A un compañero de trabajo se le ocurrió el siguiente ejemplo de SwiftUI que parece que funciona como se esperaba (puede ingresar algo de texto y se refleja a continuación), ¡pero me sorprende cómo funciona!

import SwiftUI

struct ContentView: View {
    @State var text = ""
    var body: some View {
        VStack {
            TextField("Change the string", text: $text) WrappedText(text: $text)
        }
    }
}

struct WrappedText: View {
    @Binding var text: String
    var body: some View {
        Text(text)
    }
}

Mi modelo mental de novato de SwiftUI me llevó a pensar que escribir en TextField cambiaría el $textenlace, lo que a su vez mutaría el text@State var. Esto invalidaría entonces el ContentView, lo que provocaría una nueva invocación de body. Pero curiosamente, ¡eso no es lo que sucede! Establecer un punto de interrupción en ContentView bodysolo se golpea una vez, mientras que WrappedText bodyse ejecuta cada vez que cambia el enlace. Y sin embargo, hasta donde yo sé, el textestado realmente está cambiando.

Entonces, ¿qué está pasando aquí? ¿Por qué SwiftUI no vuelve a invocar el cuerpo de ContentView en cada cambio text?

Respuestas

1 Asperi Aug 20 2020 at 04:04

En el cambio de estado, el motor de representación SwiftUI primero verifica la igualdad de vistas dentro del cuerpo y, si algunas de ellas no son iguales, llama al cuerpo para reconstruir, pero solo aquellas vistas que no son iguales. En su caso, ninguna vista depende (como valor) del textvalor (la vinculación es como una referencia, es lo mismo), por lo que no hay nada que reconstruir en este nivel. Pero en WrappedTextsu interior se detecta que Textcon new textno es igual a uno con old text, por lo que WrappedTextse llama a body of para volver a renderizar esta parte.

Esto se declara como optimización de la representación de SwiftUI, al verificar y validar la vista modificada exacta por igualdad.

De forma predeterminada, este mecanismo funciona con las propiedades de la estructura de Vista, pero podemos participar en él confirmando nuestra vista al Eqatableprotocolo y marcándola como .equatable()modificador para proporcionar una lógica más complicada para detectar si la Vista debe (o no) volverse a renderizar.