Я попробовал с previewLayout, и я понимаю, что вы имеете в виду. Тем не менее, я думаю, что поведение соответствует ожиданиям. Определение .sizeThatFits:
Установите контейнер (A) в размер предварительного просмотра (B), когда предлагается
размер устройства (C), на котором запущен предварительный просмотр.
Я вставил несколько букв, чтобы определить каждую часть и сделать ее более понятной:
A = конечный размер предварительного просмотра.
B = Размер того, что вы изменяете с помощью .previewLayout (). В первом случае это VStack. Но во втором случае это GeometryReader.
C = Размер экрана устройства.
Оба представления действуют по-разному, потому что VStack не жадный, а берет только то, что ему нужно. GeometryReader, с другой стороны, пытается получить все это, потому что он не знает, что его дочерний элемент захочет использовать. Если ребенок хочет использовать меньше, он может это сделать, но ему нужно начать с предложения всего.
Возможно, если вы отредактируете свой вопрос, чтобы точно объяснить, чего бы вы хотели достичь, я могу немного уточнить свой ответ.
Если вы хотите, чтобы GeometryReader сообщал о размере VStack. Вы можете сделать это, поместив его в модификатор .background. Но опять же, я не уверен, какова цель, так что, возможно, это не пойдет.
Я написал статью о различном использовании GeometryReader. Вот ссылка на случай, если это поможет: https://swiftui -lab.com / geometryreader-to-the-rescue /
UPDATE
Хорошо, с вашим дополнительным объяснением, у вас есть рабочее решение. Обратите внимание, что предварительный просмотр не будет работать, потому что safeInsets сообщается как ноль. На симуляторе, однако, работает нормально:
Как вы увидите, я использую настройки просмотра. Они нигде не объясняются, но я сейчас пишу о них статью, которую скоро опубликую.
Все это может выглядеть слишком многословно, но если вы обнаружите, что используете его слишком часто, вы можете заключить его в собственный модификатор.

import SwiftUI
struct InsetPreferenceKey: PreferenceKey {
static var defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
typealias Value = CGFloat
}
struct InsetGetter: View {
var body: some View {
GeometryReader { geometry in
return Rectangle().preference(key: InsetPreferenceKey.self, value: geometry.safeAreaInsets.top)
}
}
}
struct ContentView : View {
var body: some View {
MyView()
}
}
struct MyView : View {
@State private var topInset: CGFloat = 0
var body: some View {
VStack {
CustomView(inset: topInset)
.padding(.horizontal)
.padding(.bottom, 64)
.padding(.top, topInset)
.background(Color.blue)
.background(InsetGetter())
.edgesIgnoringSafeArea(.all)
.onPreferenceChange(InsetPreferenceKey.self) { self.topInset = $0 }
Spacer()
}
}
}
struct CustomView: View {
let inset: CGFloat
var body: some View {
VStack {
HStack {
Text("C \(inset)").color(.white).fontWeight(.bold).font(.title)
Spacer()
}
HStack {
Text("A").color(.white)
Text("B").color(.white)
Spacer()
}
}
}
}