Kami membentuk Lihat di SwiftUI, melanjutkan dari kondisi

Terkadang kita perlu membuat Tampilan SwiftUI, mengingat beberapa kondisi. Sebagai contoh, dalam kode di atas, kita mendefinisikan HomeView , yang mungkin berisi ProfileView , jika LogInManager memiliki loggedInUser . Kami mencoba menerapkan ini menggunakan pernyataan if standar:

struct HomeView: View {
    @ObservedObject var loginManager: LoginManager

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

            ...
        }
    }
}

Sayangnya, kode ini akan menimbulkan kesalahan saat kompilasi:
Penutupan yang berisi pernyataan aliran kontrol tidak dapat digunakan dengan pembangun fungsi ViewBuilder.

Karena fungsi tutup tidak digunakan di sini, tetapi fungsi pembuat , kami tidak dapat memasukkan kode arbitrer ke dalamnya untuk membentuk HStack atau VStack. Jadi bagaimana kita keluar dari situasi ini?

Salah satu caranya adalah dengan melewatkan pemrosesan opsional semacam itu secara langsung ke tampilan yang kami buat. Misalnya, kita dapat meneruskan ProfileView kita bukan nilai Pengguna tertentu, tetapi menjadikannya opsional:

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)
            ...
        })
    }
}

Kode ini berfungsi, tetapi tidak terlalu indah. Tidak masuk akal untuk membuat ProfileView untuk pengguna nihil. Mari kita mengambil pendekatan yang berbeda: gunakan peta untuk Pengguna opsional kami untuk mengubahnya menjadi ProfileView:

struct HomeView: View {
    @ObservedObject var loginManager: LoginManager

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

Ini sudah jauh lebih cantik: kita tidak perlu memberikan EmptyView secara manual ketika Pengguna tidak memiliki nilai. Kami kembali dapat memberikan nilai tertentu ke ProfileView, bukan opsional. Apakah mungkin melakukan lebih baik?

Berita bagus tentang @ViewBuilder adalah bahwa ini bukan semacam implementasi pribadi di SwiftUI, tetapi atribut yang dapat diakses yang dengannya kita dapat membuat anotasi fungsi dan penutupan kita sendiri.

Menggunakan atribut ini, kita dapat mengkompilasi tampilan Unwrap, yang mengambil nilai opsional sebagai parameter dan penutupan tagged @ViewBuilder untuk mengonversi nilai non-nil menjadi View:

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)
    }
}

Dengan menggunakan desain ini, kita sekarang dapat mendesain ulang seluruh struktur HomeView sepenuhnya:

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