Как получить фактический размер и положение визуализации в SwiftUI?
Вопрос в том, как получить фактический размер и положение рендеринга в родительском представлении? Другими словами, как получить реальный Text("Foo")
размер в приведенном ниже коде SwiftUI?
GeometryReaderможет использоваться для получения предложенной родительской size
и безопасной области вставки через, safeAreaInsets
и эта информация определяется внутри GeometryProxy. На скриншоте ниже видно, что предлагаемый размер VStack
- это 300
ширина и 300
высота, а фактический размер VStack
неизвестен.

struct FooView: View {
var body: some View {
GeometryReader { geometryProxy in
VStack {
Text("\(geometryProxy.size.height), \(geometryProxy.size.width)")
Text("Foo")
}
.background(Color.green)
}
.frame(width: 300, height: 300)
.background(Color.blue)
}
}
Ответы
Фактический размер визуализации
Обходной путь - получить фактический размер .background
отрисовки с помощью модификатора с вложенным GeometryReader
. Информация о размере внутри нового геометрического прокси может быть затем сохранена во временной @State
переменной, определенной в View.
struct FooSizePreferenceKey: PreferenceKey {
static let defaultValue = CGSize.zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
value = nextValue()
}
}
struct FooView: View {
@State private var fooSize: CGSize = .zero
var body: some View {
GeometryReader { geometryProxy in
VStack {
Text("\(self.fooSize.height), \(self.fooSize.width)")
Text("Foo")
.background(
GeometryReader { fooProxy in
Color
.green
.preference(key: FooSizePreferenceKey.self,
value: fooProxy.size)
.onPreferenceChange(FooSizePreferenceKey.self) { size in
self.fooSize = size
}
}
)
}
}
.frame(width: 300, height: 300)
.background(Color.blue)
}
}

Фактическая позиция визуализации
Фактическое положение визуализации для представления можно рассчитать с помощью Anchor
и anchorPreference
. Используя якорь и родительский элемент geometryProxy
, мы можем легко получить .bound
информацию о положении целевого представления.
struct FooAnchorData: Equatable {
var anchor: Anchor<CGRect>? = nil
static func == (lhs: FooAnchorData, rhs: FooAnchorData) -> Bool {
return false
}
}
struct FooAnchorPreferenceKey: PreferenceKey {
static let defaultValue = FooAnchorData()
static func reduce(value: inout FooAnchorData, nextValue: () -> FooAnchorData) {
value.anchor = nextValue().anchor ?? value.anchor
}
}
struct FooView: View {
@State private var foo: CGPoint = .zero
var body: some View {
GeometryReader { geometryProxy in
VStack {
Text("\(self.foo.x), \(self.foo.y)")
Text("Foo")
.background(
GeometryReader { fooProxy in
Color
.green
.anchorPreference(key: FooAnchorPreferenceKey.self,
value: .bounds,
transform: {FooAnchorData(anchor: $0) })
.onPreferenceChange(FooAnchorPreferenceKey.self) { data in
self.foo = geometryProxy[data.anchor!].origin
}
}
)
}
}
.frame(width: 300, height: 300)
.background(Color.blue)
}
}
