How to show list of views from a dataSource like UITableView in SwiftUI
Asked Answered
C

2

0

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

Casket answered 18/6, 2019 at 18:16 Comment(1)
Have you checked the WWDC Combine sessions? Or Session 226: Data Flor Through SwiftUI? developer.apple.com/videos/play/wwdc2019/226 That's where you should start. Pay attention to "Source of Truth", @State, @Binding` and everything else.Enwind
C
-1

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.

Casket answered 18/6, 2019 at 18:16 Comment(7)
I updated my question and answer a bit to make it more clear. Thanks for you attention.Casket
Okay, let's focus on your definition of dynamic. books in your code has three elements. Are you trying to update the List` when something/anything is updated in books? If so, have you looked into using @State? This concept is very critical to SwiftUI. As for Identifiable, I'm thinking you want to declare a UUID() element in your struct.Enwind
You are absolutely right! But books is just an example and separated from the ‘List’. For the sake of your concerns I added some comments to the code. Thanks againCasket
I'm not understanding something. Thought it was the definition of "dynamic", but maybe it's what you mean by "reusable" then. Let's say you have this books array and a SwiftUI list based on it. I your example I'd expect the list to contain "AAAA", "BBBB', and "CCCC". Then what? Are you trying to use this array in another view? And if so, to use Apple's terms, what view is the "Source of Truth"?Enwind
AH. Maybe now I get it. Are you thinking in terms of how a UITableView works? Reusable cells? IF so, have you checked out this question? #56655921 From the sounds of it, it "just works". The memory footprint indicates that you shouldn't worry about that.Enwind
Yes I meant reusing views the way UITableView does. And yes, as I researched within memory graph and UIDebugger and etc, it turns out that List view's subViews are reusable.Casket
what about different rowView in one List ?)Exstipulate
F
0

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

…
Fontainebleau answered 22/6, 2019 at 20:23 Comment(0)
C
-1

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.

Casket answered 18/6, 2019 at 18:16 Comment(7)
I updated my question and answer a bit to make it more clear. Thanks for you attention.Casket
Okay, let's focus on your definition of dynamic. books in your code has three elements. Are you trying to update the List` when something/anything is updated in books? If so, have you looked into using @State? This concept is very critical to SwiftUI. As for Identifiable, I'm thinking you want to declare a UUID() element in your struct.Enwind
You are absolutely right! But books is just an example and separated from the ‘List’. For the sake of your concerns I added some comments to the code. Thanks againCasket
I'm not understanding something. Thought it was the definition of "dynamic", but maybe it's what you mean by "reusable" then. Let's say you have this books array and a SwiftUI list based on it. I your example I'd expect the list to contain "AAAA", "BBBB', and "CCCC". Then what? Are you trying to use this array in another view? And if so, to use Apple's terms, what view is the "Source of Truth"?Enwind
AH. Maybe now I get it. Are you thinking in terms of how a UITableView works? Reusable cells? IF so, have you checked out this question? #56655921 From the sounds of it, it "just works". The memory footprint indicates that you shouldn't worry about that.Enwind
Yes I meant reusing views the way UITableView does. And yes, as I researched within memory graph and UIDebugger and etc, it turns out that List view's subViews are reusable.Casket
what about different rowView in one List ?)Exstipulate

© 2022 - 2024 — McMap. All rights reserved.