SwiftUI Reusable View melalui Protocol Inheritance

Oct 25 2020

Hasil

Tujuan saya adalah membuat protokol tampilan yang dapat digunakan kembali yang mewarisi protokol Tampilan SwiftUI dan menyediakan beberapa fungsionalitas default, untuk memuat tampilan yang berbeda berdasarkan status konten tampilan.

Dengan cara ini, saya tidak perlu menulis ulang kode identik untuk setiap tampilan yang saya buat dan membuat kode lebih bersih.

Masalah

Saya membuat protokol "DelayedContentView" yang menampilkan dua badan tampilan berbeda berdasarkan apakah konten tampilan telah dimuat. Masalah muncul ketika saya mencoba menerapkannya. Properti "loadedBody" dan "unloadedBody" tidak bisa berjenis "some View" meskipun mereka memiliki tipe yang sama dengan RelatedType Badan Tampilan SwiftUI.

Latar Belakang

Saya memiliki berbagai tampilan di aplikasi saya yang mengambil data dari jarak jauh. Setiap tampilan menampilkan dua badan tampilan: satu untuk saat konten sedang diambil dan yang lainnya untuk saat pengambilan selesai.

Protokol Tampilan SwiftUI Apple

public protocol View {

    associatedtype Body : View

    var body: Self.Body { get }
}

Protokol Tampilan Saya

protocol DelayedContentView:View {
    
    func contentLoaded() -> Bool
    
    var loadedBody: Self.Body { get }
    
    var unloadedBody: Self.Body { get }
    
}


extension DelayedContentView {
    
    //Default implementation that I won't have to rewrite for each view.
    var body: some View {
        if contentLoaded() {
            return self.loadedBody
        }else{
            return self.unloadedBody
        }
    }
    
}

Penerapan

struct ExampleView:DelayedContentView {
    
    func contentLoaded() -> Bool {
        //Ask viewModel if content is loaded.
        return false
    }

    var loadedBody: some View {
        Text("Content is loaded.")
    }

    var unloadedBody: some View {
        Text("Fetching content...")
    }
        
    
}
Compile Error: Type 'ExampleView' does not conform to protocol 'DelayedContentView'

Saya pikir warisan protokol adalah alat yang tepat untuk kasus seperti ini, tetapi mungkin saya salah?

Jawaban

2 NewDev Oct 25 2020 at 10:31

Protokol DelayedContentViewharus menambahkan dua jenis lagi yang terkait untuk mewakili dua jenis tampilan tambahan: satu untuk tampilan "dimuat" dan satu - untuk "dibongkar". Ini tidak bisa Self.Bodykarena jelas akan dilanggar karena Anda membutuhkan tampilan bersyarat dalam isi.

protocol DelayedContentView: View {
    
    associatedtype LoadedBody: View
    associatedtype UnloadedBody: View
    
    func contentLoaded() -> Bool
    
    var loadedBody: LoadedBody { get }
    
    var unloadedBody: UnloadedBody { get }
}

Sekarang Anda memiliki dua tipe generik, Self.Bodyakan menjadi tipe gabungan dari keduanya:

extension DelayedContentView {

    @ViewBuilder // needed to create a _ConditionalContent type from if/else
    var body: some View {
        if contentLoaded() {
            self.loadedBody
        } else {
            self.unloadedBody
        }
    }
}

Sekarang, Tampilan spesifik Anda akan mendeklarasikan apa LoadedViewdan UnloadedViewjenisnya. Misalnya, di Anda ExampleView, mereka akan menjadi Text, dan Bodyakan menjadi _ConditionalContent<Text,Text>.