SwiftUI ScrollView VStack GeometryReader altezza ignorata
Voglio usare uno ScrollView al di fuori di un VStack, in modo che il mio contenuto sia scorrevole se VStack si espande oltre le dimensioni dello schermo. Ora voglio utilizzare GeometryReaderall'interno del VStack e causa problemi, che posso risolvere solo impostando il frame GeometryReader, che non mi aiuta molto dato che uso il lettore per definire la dimensione della vista.
Ecco il codice senza ScrollView e funziona bene:
struct MyExampleView: View {
var body: some View {
VStack {
Text("Top Label")
.background(Color.red)
GeometryReader { reader in
Text("Custom Sized Label")
.frame(width: reader.size.width, height: reader.size.width * 0.5)
.background(Color.green)
}
Text("Bottom Label")
.background(Color.blue)
}
.background(Color.yellow)
}
}
Ciò si traduce nella seguente immagine:
L'etichetta di dimensioni personalizzate deve essere a larghezza intera, ma metà della larghezza per l'altezza. Ora se inserisco lo stesso codice in una ScrollView, questo accade:
Non solo tutto è diventato più piccolo, ma l'altezza dell'etichetta di dimensioni personalizzate viene in qualche modo ignorata. Se imposto l'altezza del GeometryReader, posso regolare quel comportamento, ma voglio che GeometryReader cresca tanto quanto il suo contenuto. Come posso raggiungere questo obiettivo?
Grazie
Risposte
Dovrebbe essere chiaro che GeometryReadernon è uno strumento magico, legge solo lo spazio disponibile nel contesto corrente genitore, ma ... ScrollViewnon ha il proprio spazio disponibile, è zero, perché determina lo spazio necessario dal contenuto interno ... quindi usando GeometryReaderqui hai un ciclo - il bambino chiede al genitore la dimensione, ma il genitore si aspetta la dimensione dal figlio ... Il renderer SwiftUI in qualche modo risolve questo problema (trovando le dimensioni minime conosciute), solo per non bloccarsi.
Ecco la possibile soluzione per il tuo scenario: lo strumento appropriato qui è visualizzare le preferenze. Preparato e testato con Xcode 12 / iOS 14.
struct DemoLayout_Previews: PreviewProvider {
static var previews: some View {
Group {
MyExampleView()
ScrollView { MyExampleView() }
}
}
}
struct MyExampleView: View {
@State private var height = CGFloat.zero
var body: some View {
VStack {
Text("Top Label")
.background(Color.red)
Text("Custom Sized Label")
.frame(maxWidth: .infinity)
.background(GeometryReader {
// store half of current width (which is screen-wide)
// in preference
Color.clear
.preference(key: ViewHeightKey.self,
value: $0.frame(in: .local).size.width / 2.0) }) .onPreferenceChange(ViewHeightKey.self) { // read value from preference in state self.height = $0
}
.frame(height: height) // apply from stored state
.background(Color.green)
Text("Bottom Label")
.background(Color.blue)
}
.background(Color.yellow)
}
}
struct ViewHeightKey: PreferenceKey {
typealias Value = CGFloat
static var defaultValue = CGFloat.zero
static func reduce(value: inout Value, nextValue: () -> Value) {
value += nextValue()
}
}
Nota: ... e non utilizzare GeometryReader se non sei sicuro del contesto in cui si trova la tua vista.