Como obter o tamanho e a posição reais da visualização renderizada no SwiftUI?
A questão é como obter o tamanho real renderizado e a posição visualizada em uma visualização pai? Em outras palavras, como obter o Text("Foo")
tamanho real no código SwiftUI abaixo?
GeometryReaderpode ser usado para size
inserir a área segura e proposta pelos pais por meio de safeAreaInsets
e essas informações são definidas internamente GeometryProxy. Você pode ver na captura de tela abaixo, o tamanho proposto para VStack
é 300
largura e 300
altura e o tamanho real para VStack
é desconhecido.
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)
}
}
Respostas
Tamanho real renderizado
A solução alternativa é obter o tamanho real renderizado por meio do .background
modificador com um aninhado GeometryReader
. As informações de tamanho dentro do novo proxy de geometria podem ser armazenadas em uma @State
variável temporária definida na Visualização.
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)
}
}
Posição real renderizada
A posição real renderizada para a vista pode ser calculada usando Anchor
e anchorPreference
. Usando a âncora e o pai geometryProxy
, podemos obter facilmente as .bound
informações de posição da visualização de destino.
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)
}
}