Altura do SwiftUI ScrollView VStack GeometryReader ignorada
Quero usar um ScrollView fora de um VStack, para que meu conteúdo seja rolável se o VStack se expandir além do tamanho da tela. Agora quero usar GeometryReaderdentro do VStack e isso causa problemas, que só posso resolver configurando o quadro GeometryReader, o que realmente não me ajuda, já que uso o leitor para definir o tamanho da visualização.
Aqui está o código sem ScrollView e funciona muito bem:
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)
}
}
Isso resulta na seguinte imagem:
A etiqueta de tamanho personalizado deve ter largura total, mas metade da largura para a altura. Agora, se eu envolver o mesmo código em um ScrollView, isso acontece:
Não apenas tudo ficou menor, mas a altura do rótulo de tamanho personalizado é de alguma forma ignorada. Se eu definir a altura do GeometryReader, posso ajustar esse comportamento, mas quero que o GeometryReader cresça tanto quanto seu conteúdo. Como posso conseguir isso?
obrigado
Respostas
Deve ser entendido que GeometryReadernão é uma ferramenta mágica, ela apenas lê o espaço disponível no contexto pai atual, mas ... ScrollViewnão tem espaço próprio disponível, é zero, porque determina o espaço necessário do conteúdo interno ... então usando GeometryReaderaqui você tem ciclo - o filho pergunta ao pai o tamanho, mas o pai espera o tamanho do filho ... O renderizador SwiftUI de alguma forma resolve isso (encontrando tamanhos mínimos conhecidos), apenas para não travar.
Aqui está uma solução possível para o seu cenário - o instrumento apropriado aqui são as preferências de visualização. Preparado e testado com 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 não use GeometryReader se você não tiver certeza sobre o contexto em que sua visualização está.