SwiftUI ScrollView VStack GeometryReader Wysokość zignorowana
Chcę użyć ScrollView poza VStack, aby moja zawartość była przewijalna, jeśli VStack rozszerzy się poza rozmiar ekranu. Teraz chcę używać GeometryReaderw VStack i powoduje to problemy, które mogę rozwiązać tylko ustawiając ramkę GeometryReader, co tak naprawdę nie pomaga, biorąc pod uwagę, że używam czytnika do definiowania rozmiaru widoku.
Oto kod bez ScrollView i działa ładnie:
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)
}
}
Powoduje to następujący obraz:
Etykieta o niestandardowym rozmiarze powinna mieć pełną szerokość, ale połowę szerokości dla wysokości. Teraz, jeśli zawijam ten sam kod w ScrollView, dzieje się tak:
Nie tylko wszystko się zmniejszyło, ale wysokość etykiety niestandardowego rozmiaru jest w jakiś sposób ignorowana. Jeśli ustawię wysokość GeometryReader, mogę dostosować to zachowanie, ale chcę, aby GeometryReader rozrósł się do jego zawartości. Jak mogę to osiągnąć?
Dzięki
Odpowiedzi
Należy rozumieć, że GeometryReadernie jest to magiczne narzędzie, po prostu odczytuje dostępne miejsce w bieżącym kontekście rodzica, ale ... ScrollViewnie ma własnego wolnego miejsca, jest zerowe, ponieważ określa potrzebną przestrzeń z zawartości wewnętrznej ... więc używając GeometryReadertutaj masz cykl - dziecko pyta rodzica o rozmiar, ale rodzic oczekuje rozmiaru od dziecka ... Renderowanie SwiftUI w jakiś sposób rozwiązuje ten problem (znajdując minimalne znane rozmiary), aby się nie zawiesić.
Oto możliwe rozwiązanie dla twojego scenariusza - odpowiednim instrumentem są tutaj preferencje widoku. Przygotowano i przetestowano z 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()
}
}
Uwaga: ... i nie używaj GeometryReader, jeśli nie jesteś pewien kontekstu, w jakim się znajdujesz.