Swiftui - Jak zainicjować zaobserwowany obiekt przy użyciu obiektu środowiska jako parametru?

9

Nie jestem pewien, czy jest to antypattern w tym odważnym nowym świecie SwiftUI, w którym żyjemy, ale zasadniczo mam @EnvironmentObject z zapisanymi podstawowymi informacjami dla użytkownika, które mogą wywoływać moje poglądy.

Mam również @ObservedObject, który posiada niektóre dane wymagane dla tego widoku.

Kiedy pojawi się widok, chcę użyć tego @EnvironmentObject do zainicjowania @ObservedObject:

struct MyCoolView: View { 

    @EnvironmentObject userData: UserData
    @ObservedObject var viewObject: ViewObject = ViewObject(id: self.userData.UID)  

    var body: some View { 
            Text("\(self.viewObject.myCoolProperty)")
    } 
}

Niestety nie mogę wywołać self w zmiennej środowiskowej, dopóki nie zostanie zainicjowana:

„Nie można użyć elementu członkowskiego„ userData ”instancji w inicjalizatorze właściwości; inicjatory właściwości są uruchamiane, zanim będzie dostępne„ self ”.”

Widzę kilka możliwych dróg naprzód, ale wszystkie wydają się hackami. Jak mam do tego podejść?

snarik
źródło
Może możesz spróbować dodać niestandardową initstrukturę.
nayem
Próbowałem tego i dostałem nieco dziwny błąd: Property wrappers are not yet supported on local properties Zasadniczo to powiedzenie, że nie mogę utworzyć @ObservedObject w metodzie init.
snarik

Odpowiedzi:

9

Oto podejście (najprostsze IMO):

struct MyCoolView: View {
    @EnvironmentObject var userData: UserData

    var body: some View {
        MyCoolInternalView(ViewObject(id: self.userData.UID))
    }
}

struct MyCoolInternalView: View {
    @EnvironmentObject var userData: UserData
    @ObservedObject var viewObject: ViewObject

    init(_ viewObject: ViewObject) {
        self.viewObject = viewObject
    }

    var body: some View {
            Text("\(self.viewObject.myCoolProperty)")
    }
}
Asperi
źródło
To jest doskonałe. MyCoolView był w rzeczywistości dzieckiem widoku „domowego”, w którym zadeklarowałem obiekt ObservedObject. Dzięki!
snarik
Ale co, jeśli chcesz manipulować danymi użytkownika w ViewObject bez tworzenia nowego ViewObject za każdym razem?
BobiSad
0

zamiast tworzyć widok podrzędny, możesz dodać do niego atrapę inicjatora, aby "ViewObject"móc go wywołać przed wywołaniem prawdziwego inicjalizatora

struct MyCoolView: View { 

    @EnvironmentObject userData: UserData
    @ObservedObject var viewObject: ViewObject

    init() {
        viewObject = ViewObject()
        defer {
            viewObject = ViewObject(id: self.userData.UID)
        }
    }

    var body: some View { 
            Text("\(self.viewObject.myCoolProperty)")
    } 
}

dla przypomnienia, nie testowałem tego

Aleyam
źródło
0

Oto prosty sposób:

struct MyCoolView: View {
    @EnvironmentObject var userData: UserData

    var body: some View {
        Observe(obj: ViewObject(id: userData.UID)) { viewObject in
             Text("\(viewObject.myCoolProperty)")
        }
    }
}

Dzięki temu pomocnikowi, który sprawia, że ​​działa:

struct Observe<T: ObservableObject, V: View>: View {
    @ObservedObject var obj: T
    let content: (T) -> V
    var body: some View { content(obj) }
}
ccwasden
źródło