Comment obtenir la taille et la position réelles de la vue dans SwiftUI?
La question est de savoir comment obtenir la taille et la position du rendu réel affiché dans une vue parent? En d'autres termes, comment obtenir la Text("Foo")
taille réelle dans le code SwiftUI ci-dessous?
GeometryReaderpeut être utilisé pour obtenir l' size
insertion de la zone de sécurité proposée par le parent via safeAreaInsets
et ces informations sont définies à l'intérieur GeometryProxy. Vous pouvez voir sur la capture d'écran ci-dessous, la taille proposée VStack
est la 300
largeur et la 300
hauteur et la taille réelle du VStack
est inconnue.
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)
}
}
Réponses
Taille réelle du rendu
La solution de contournement consiste à obtenir la taille réelle du rendu via un .background
modificateur avec un GeometryReader
. Les informations de taille à l'intérieur du nouveau proxy de géométrie peuvent ensuite être stockées dans une @State
variable temporaire définie dans la vue.
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)
}
}
Position réelle rendue
La position de rendu réelle de la vue peut être calculée à l'aide de Anchor
et anchorPreference
. En utilisant l'ancre et le parent geometryProxy
, nous pouvons facilement obtenir les .bound
informations de position de la vue cible.
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)
}
}