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' sizeinsertion de la zone de sécurité proposée par le parent via safeAreaInsetset ces informations sont définies à l'intérieur GeometryProxy. Vous pouvez voir sur la capture d'écran ci-dessous, la taille proposée VStackest la 300largeur et la 300hauteur et la taille réelle du VStackest 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 .backgroundmodificateur avec un GeometryReader. Les informations de taille à l'intérieur du nouveau proxy de géométrie peuvent ensuite être stockées dans une @Statevariable 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 Anchoret anchorPreference. En utilisant l'ancre et le parent geometryProxy, nous pouvons facilement obtenir les .boundinformations 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)
}
}