How to show list of views from a dataSource like U

2020-05-06 13:06发布

问题:

In SwiftUI, we have List to represent reusable items. Just like UITableView in UIKit.

Static lists builds like this:

List {
    Text("cell")
    Text("cell")
    Text("cell")
    Text("cell")
}

But seems like it's not reusable at all

How can I have an array of some objects and fill the list based on the array and its dynamic size (count)?

回答1:

Cell are reused. See Does the List in SwiftUI reuse cells similar to UITableView?

For static Lists the limit are 10 Items. This has to do with the ViewBuilder implementation.

extension ViewBuilder {

    public static func buildBlock<C0, C1>(_ c0: C0, _ c1: C1) -> TupleView<(C0, C1)> where C0 : View, C1 : View
}

…

extension ViewBuilder {

    public static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9>(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7: C7, _ c8: C8, _ c9: C9) -> TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9)> where C0 : View, C1 : View, C2 : View, C3 : View, C4 : View, C5 : View, C6 : View, C7 : View, C8 : View, C9 : View
}

To use an array you can use this API:


let array = [1,2,3,4]

let listView = List(array) { value in
    Text(value.description)
}

extension List {

extension List {

    /// Creates a List that computes its rows on demand from an underlying
    /// collection of identified data.
    @available(watchOS, unavailable)
    public init<Data, RowContent>(_ data: Data, selection: Binding<Selection>?, rowContent: @escaping (Data.Element.IdentifiedValue) -> RowContent) where Content == ForEach<Data, HStack<RowContent>>, Data : RandomAccessCollection, RowContent : View, Data.Element : Identifiable

…


回答2:

Dynamic views usually generated from dynamic data. So you should consider using a data structure for your repeating views, then build the list based on data like this:

struct Student: Identifiable {
    let name: String
    let id: Int
}

struct ContentView : View {

    // Could be `@State Var` instead
    let students = [
        Student(name: "AAAAA", id: 1),
        Student(name: "BBBBB", id: 2),
        Student(name: "CCCCC", id: 3), // Notice that trailing comma is not problem here? 
    ]

    var body: some View {
        List(students) { student in
            Text(student.name)
        }
    }
}

Array should contain Identifiable objects (Recommended)

or if you not prefer to conform to Identifiable protocol you can use it like this:

struct Book {
    let anyPropertyName: String
    let title: String
}

struct ContentView : View {

    // Could be `@State Var` instead
    let books = [
        Book(anyPropertyName: "AAAA", title: "1111"),
        Book(anyPropertyName: "BBBB", title: "2222"),
        Book(anyPropertyName: "CCCC", title: "3333")
    ]

    var body: some View {
        List(books.identified(by: \.anyPropertyName)) { book in
            Text(book.title)
        }
    }
}

Note that dataSource can be @State var and it gives the ability to update the UI whenever any @State var changes.

Lastly, although it seems like it's not reusing, but actually it is! The limit of 10 static items has nothing to do with reusing.