SwiftUI ScrollView VStack GeometryReader altura ignorada
Quiero usar un ScrollView fuera de un VStack, de modo que mi contenido se pueda desplazar si el VStack se expande más allá del tamaño de la pantalla. Ahora quiero usar GeometryReaderdentro del VStack y causa problemas, que solo puedo resolver configurando el marco GeometryReader, lo que realmente no me ayuda dado que uso el lector para definir el tamaño de la vista.
Aquí está el código sin ScrollView y funciona muy bien:
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)
}
}
Esto da como resultado la siguiente imagen:
La etiqueta de tamaño personalizado debe tener el ancho completo, pero la mitad del ancho para la altura. Ahora, si envuelvo el mismo código en un ScrollView, sucede esto:
No solo todo se hizo más pequeño, sino que de alguna manera se ignora la altura de la etiqueta de tamaño personalizado. Si configuro la altura de GeometryReader, puedo ajustar ese comportamiento, pero quiero que GeometryReader crezca tanto como su contenido. ¿Cómo puedo conseguir esto?
Gracias
Respuestas
Debe entenderse que GeometryReaderno es una herramienta mágica, solo lee el espacio disponible en el contexto actual padre, pero ... ScrollViewno tiene espacio propio disponible, es cero, porque determina el espacio necesario a partir del contenido interno ... así que usar GeometryReaderaquí tienes ciclo - el niño pregunta al padre por el tamaño, pero el padre espera el tamaño del niño ... El renderizador SwiftUI de alguna manera resuelve esto (encontrando tamaños mínimos conocidos), solo para no fallar.
Aquí hay una posible solución para su escenario: el instrumento apropiado aquí son las preferencias de visualización. Preparado y probado 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: ... y no use GeometryReader si no está seguro del contexto en el que se encuentra su vista.