Saltar al contenido

¿Cómo paso datos de una vista secundaria a una vista principal a otra vista secundaria en SwiftUI?

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.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *