Wir bilden View in SwiftUI, ausgehend von den Bedingungen

Manchmal müssen wir unter bestimmten Bedingungen eine SwiftUI-Ansicht erstellen. Zum Beispiel in dem obigen Code definieren wir eine Homeview , die eine enthalten ProfileView , wenn Loginmanager hat loggedInUser . Wir versuchen dies mit der Standard-if-Anweisung zu implementieren:

struct HomeView: View {
    @ObservedObject var loginManager: LoginManager

    var body: some View {
        VStack {
            if let user = loginManager.loggedInUser {
                ProfileView(user: user)
            }

            ...
        }
    }
}

Leider wird dieser Code beim Kompilieren einen Fehler auslösen:
Der Abschluss mit der Kontrollflussanweisung kann nicht mit dem Funktionsgenerator ViewBuilder verwendet werden.

Da hier keine Funktionsschließer , sondern Funktionsersteller verwendet werden, können wir keinen beliebigen Code in sie einfügen, um einen HStack oder VStack zu bilden. Wie kommen wir aus dieser Situation heraus?

Eine Möglichkeit besteht darin, die Verarbeitung solcher Optionen direkt an die von uns erstellte Ansicht zu übergeben. Zum Beispiel können wir in unserer Profilansicht keinen bestimmten Benutzerwert übergeben, sondern ihn optional machen:

struct ProfileView: View {
    var user: User?

    var body: some View {
        guard let user = user else {
            // We have to use 'AnyView' to perform type erasure here,
            // in order to give our 'body' a single return type:
            return AnyView(EmptyView())
        }

        return AnyView(VStack {
            Text(user.name)
            ...
        })
    }
}

Dieser Code funktioniert, ist aber nicht besonders schön. Es macht keinen Sinn, eine Profilansicht für Benutzer Null zu erstellen. Gehen wir anders vor: Verwenden Sie die Karte für unseren optionalen Benutzer, um sie in ProfileView zu konvertieren:

struct HomeView: View {
    @ObservedObject var loginManager: LoginManager

    var body: some View {
        VStack {
            loginManager.loggedInUser.map { user in
                ProfileView(user: user)
            }
            ...
        }
    }
}

Dies ist bereits viel schöner: Wir müssen EmptyView nicht manuell angeben, wenn der Benutzer keinen Wert hat. Wir können wieder einen bestimmten Wert an die Profilansicht übergeben, der nicht optional ist. Kann man es noch besser machen?

Die gute Nachricht bei @ViewBuilder ist, dass dies keine private Implementierung in SwiftUI ist, sondern ein zugängliches Attribut, mit dem wir unsere eigenen Funktionen und Abschlüsse kommentieren können.

Mit diesem Attribut können wir eine Ansicht Unwrap kompilieren, die einen optionalen Wert als Parameter und einen mit @ViewBuilder gekennzeichneten Abschluss verwendet, um einen Wert ungleich Null in Ansicht zu konvertieren:

struct Unwrap<Value, Content: View>: View {
    private let value: Value?
    private let contentProvider: (Value) -> Content

    init(_ value: Value?,
         @ViewBuilder content: @escaping (Value) -> Content) {
        self.value = value
        self.contentProvider = content
    }

    var body: some View {
        value.map(contentProvider)
    }
}

Mit diesem Design können wir jetzt die gesamte Struktur von HomeView komplett neu gestalten:

struct HomeView: View {
    @ObservedObject var loginManager: LoginManager

    var body: some View {
        VStack {
            Unwrap(loginManager.loggedInUser) { user in
                HStack {
                    Text("Logged in as:")
                    ProfileView(user: user)
                }
            }
            ...
        }
    }
} 

All Articles