SwiftUI ScrollView VStack GeometryReader высота игнорируется
Я хочу использовать ScrollView вне VStack, чтобы мой контент можно было прокручивать, если VStack выходит за пределы размера экрана. Теперь я хочу использовать его GeometryReaderв VStack, и это вызывает проблемы, которые я могу решить, только установив фрейм GeometryReader, что мне не очень помогает, учитывая, что я использую средство чтения для определения размера представления.
Вот код без ScrollView, и он отлично работает:
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)
}
}
В результате получается следующее изображение:
Этикетка нестандартного размера должна быть полной ширины, но половина ширины - высоты. Теперь, если я заключу тот же код в ScrollView, произойдет следующее:
Мало того, что все стало меньше, но и высота ярлыка нестандартного размера как-то игнорируется. Если я установлю высоту GeometryReader, я могу настроить это поведение, но я хочу, чтобы GeometryReader увеличивался до размера его содержимого. Как я могу этого добиться?
благодаря
Ответы
Следует понимать, что GeometryReaderэто не волшебный инструмент, он просто читает доступное пространство в текущем родительском контексте, но ... ScrollViewне имеет собственного доступного пространства, он равен нулю, потому что он определяет необходимое пространство из внутреннего содержимого ... поэтому, используя GeometryReaderздесь у вас есть цикл - дочерний элемент запрашивает размер у родителя, но родитель ожидает его размера ... Средство визуализации SwiftUI каким-то образом решает эту проблему (находит минимальные известные размеры), просто чтобы не сбой.
Вот возможное решение для вашего сценария - подходящий инструмент здесь - это настройки просмотра. Подготовлено и протестировано с 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()
}
}
Примечание: ... и не используйте GeometryReader, если вы не уверены в контексте, в котором находится ваше представление.