¿Cómo funciona este ejemplo de enlace + estado de SwiftUI sin volver a invocar el cuerpo?
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 $text
enlace, 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 body
solo se golpea una vez, mientras que WrappedText body
se ejecuta cada vez que cambia el enlace. Y sin embargo, hasta donde yo sé, el text
estado realmente está cambiando.
Entonces, ¿qué está pasando aquí? ¿Por qué SwiftUI no vuelve a invocar el cuerpo de ContentView en cada cambio text
?
Respuestas
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 text
valor (la vinculación es como una referencia, es lo mismo), por lo que no hay nada que reconstruir en este nivel. Pero en WrappedText
su interior se detecta que Text
con new text
no es igual a uno con old text
, por lo que WrappedText
se 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 Eqatable
protocolo 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.