Este artículo fue aprobado por especialistas así aseguramos la veracidad de esta reseña.
Solución:
Puede usar EnvironmentObject para cosas como esta …
Lo bueno de EnvironmentObject es que siempre que y donde sea que cambie una de sus variables, siempre se asegurará de que en todos los lugares donde se use esa variable, se actualice.
Nota: Para que todas las variables se actualicen, debe asegurarse de pasar por la clase BindableObject / EnvironmentObject en cada Vista en la que desea que se actualice en …
SourceRectBindings:
class SourceRectBindings: BindableObject {
/* PROTOCOL PROPERTIES */
var willChange = PassthroughSubject()
/* Seems Like you missed the 'self' (send(self)) */
var sourceRect: CGRect = .zero willSet willChange.send(self)
Padre:
struct ParentView: view
@EnvironmentObject var sourceRectBindings: SourceRectBindings
var body: some View
childViewA()
.environmentObject(sourceRectBindings)
childViewB()
.environmentObject(sourceRectBindings)
ChildViewA:
struct ChildViewA: view
@EnvironmentObject var sourceRectBindings: SourceRectBindings
var body: some View
// Update your frame and the environment...
// ...will update the variable everywhere it was used
self.sourceRectBindings.sourceRect = CGRect(x: 0, y: 0, width: 300, height: 400)
return Text("From ChildViewA : (self.sourceRectBindings.sourceRect)")
ChildViewB:
struct ChildViewB: view
@EnvironmentObject var sourceRectBindings: SourceRectBindings
var body: some View
// This variable will always be up to date
return Text("From ChildViewB : (self.sourceRectBindings.sourceRect)")
ÚLTIMOS PASOS PARA HACERLO FUNCIONAR
• Vaya a la vista más alta, desea que se actualice la variable, que es su vista principal, pero generalmente prefiero mi SceneDelegate … así que así es como suelo hacerlo:
let sourceRectBindings = SourceRectBindings()
• Luego, en la misma clase, ‘SceneDelegate’ es ir a donde especifico mi vista raíz y pasar por mi EnviromentObject
window.rootViewController = UIHostingController(
rootView: ContentView()
.environmentObject(sourceRectBindings)
)
• Luego, para el último paso, lo paso a la vista de los padres.
struct ContentView : View
@EnvironmentObject var sourceRectBindings: SourceRectBindings
var body: some View
ParentView()
.environmentObject(sourceRectBindings)
Si ejecuta el código así, verá que su vista previa arroja errores. Eso es porque no pasó la variable de entorno. Para hacerlo, simplemente inicie una instancia ficticia de su entorno:
#if DEBUG
struct ContentView_Previews : PreviewProvider
static var previews: some View
ContentView()
.environmentObject(SourceRectBindings())
#endif
Ahora puede usar esa variable como quiera y si desea pasar algo más que el rect … puede simplemente agregar la variable a la clase EnvironmentObject y puede acceder y actualizarla
No mencionó cómo ChildViewB obtiene su rect. Es decir, ¿utiliza un GeometryReader o cualquier otra fuente? Independientemente del método que utilice, puede pasar información hacia arriba en la jerarquía de dos formas: mediante un enlace o mediante preferencias. Y si la geometría está involucrada, incluso las preferencias de anclaje.
Para el primer caso, sin ver su implementación, es posible que le falte un DispatchQueue.main.async
al configurar el rect. Esto se debe a que el cambio debe ocurrir. después la vista terminó de actualizarse. Sin embargo, considero que es un truco sucio y sucio y solo lo uso durante las pruebas, pero nunca en el código de producción.
La segunda alternativa (usar preferencias) es un enfoque más sólido (y se prefiere ;-). Incluiré el código para ambos enfoques, pero definitivamente deberías aprender más sobre las preferencias. Recientemente escribí un artículo, que puede encontrar aquí: https://swiftui-lab.com/communicating-with-the-view-tree-part-1/
Primer método: No recomiendo pasar los valores de geometría hacia arriba en la jerarquía, eso afectará la forma en que se dibujan otras vistas. Pero si insiste, así es como puede hacer que funcione:
struct ContentView : View
@State var rectFrame: CGRect = .zero
var body: some View
VStack
ChildViewA(rectFrame: $rectFrame)
ChildViewB(rectFrame: rectFrame)
struct ChildViewA: View
@Binding var rectFrame: CGRect
var body: some View
DispatchQueue.main.async
self.rectFrame = CGRect(x: 10, y: 20, width: 30, height: 40)
return Text("CHILD VIEW A")
struct ChildViewB: View
var rectFrame: CGRect = CGRect.zero
var body: some View
Text("CHILD VIEW B: RECT WIDTH = (rectFrame.size.width)")
Segundo método: Uso de preferencias
import SwiftUI
struct MyPrefKey: PreferenceKey
typealias Value = CGRect
static var defaultValue: CGRect = .zero
static func reduce(value: inout CGRect, nextValue: () -> CGRect)
value = nextValue()
struct ContentView : View
@State var rectFrame: CGRect = .zero
var body: some View
VStack
ChildViewA()
ChildViewB(rectFrame: rectFrame)
.onPreferenceChange(MyPrefKey.self)
self.rectFrame = $0
struct ChildViewA: View
var body: some View
return Text("CHILD VIEW A")
.preference(key: MyPrefKey.self, value: CGRect(x: 10, y: 20, width: 30, height: 40))
struct ChildViewB: View
var rectFrame: CGRect = CGRect.zero
var body: some View
Text("CHILD VIEW B: RECT WIDTH = (rectFrame.size.width)")
Si te gustó nuestro trabajo, puedes dejar un escrito acerca de qué te ha impresionado de este tutorial.