In che modo questo esempio di associazione + stato SwiftUI riesce a funzionare senza richiamare nuovamente il corpo?
Un collega ha escogitato il seguente esempio SwiftUI che sembra funzionare proprio come previsto (puoi inserire del testo e viene rispecchiato di seguito), ma il modo in cui funziona è sorprendente per me!
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)
}
}
Il mio modello mentale da principiante di SwiftUI mi ha portato a pensare che digitando nel TextField cambierebbe l' $text
associazione, che a sua volta avrebbe mutato la text
@State var. Ciò quindi invaliderebbe il ContentView
, innescando una nuova invocazione di body
. Ma è interessante notare che non è quello che succede! L'impostazione di un punto di interruzione in ContentView body
viene raggiunta solo una volta, mentre WrappedText body
viene eseguita ogni volta che l'associazione cambia. Eppure, per quanto ne so, lo text
stato sta davvero cambiando.
Allora, cosa sta succedendo qui? Perché SwiftUI non richiama nuovamente il corpo di ContentView ad ogni modifica a text
?
Risposte
Al cambio di stato, il motore di rendering SwiftUI controlla inizialmente l'uguaglianza delle viste all'interno del corpo e, se alcune di esse non sono uguali, chiama body per ricostruire, ma solo quelle viste non uguali. Nel tuo caso nessuna vista dipende (come valore) dal text
valore (il legame è come un riferimento - è lo stesso), quindi niente da ricostruire a questo livello. Ma all'interno WrappedText
si rileva che Text
con nuovo text
non è uguale a uno con vecchio text
, quindi body of WrappedText
è chiamato a ri-renderizzare questa parte.
Questa è dichiarata ottimizzazione del rendering di SwiftUI, controllando e convalidando la visualizzazione esatta modificata per uguaglianza.
Per impostazione predefinita, questo meccanismo funziona con le proprietà della struttura della vista, ma possiamo essere coinvolti confermando la nostra vista sul Eqatable
protocollo e contrassegnandola come .equatable()
modificatore per fornire una logica più complicata per rilevare se View debba essere (o non essere) ri-renderizzato.