ios – SwiftUI: ui slots and slot props?


I’m starting with Swift (coming from front-end) with test projects and have a question about view and view builders

For reference: in a front-end framework Vue.js there is a concept of slots and slot props https://vuejs.org/guide/components/slots.html#scoped-slots
Same goes for Svelte https://svelte.dev/docs/special-elements#slot and also same functionality can be achieved in React

For example I have some generic list that has some basic layout and makes some logic (filtration, sorting etc) inside and I want to use it to pass a list and display list items however I want, and if I won’t provide a builder, there will be some default view that is specified inside that list

Is there any way to achieve such functionality in SwiftUI/UIKit?

What I’ve tried to do is something like the next code, I simplified it to show the general idea

import SwiftUI

struct ListTestView\<T: Hashable, Content: View\>: View {
    private let items: \[T\]
    private let listItemBuilder: (ListItemBuilderParams) -\> Content

    init(
        _ list: [T],
        @ViewBuilder listItem: @escaping (_ params: ListItemBuilderParams) -> Content
    ) {
        self.items = list
        self.listItemBuilder = listItem
    }
    
    var body: some View {
        VStack {
            ForEach(filteredItems, id: \.self) { item in
                listItemBuilder(.init(item1: item, item2: item, item3: item)) // ?? Text(String(item))
            }
        }
    }
    
    private var filteredItems: [T] {
        // ...some filtration
        return items
    }
    
    struct ListItemBuilderParams {
        let item1: T
        let item2: T
        let item3: T
        // ... and possible more
    }
}

struct ViewWithList: View {
    var body: some View {
        ListTestView(\[1, 2, 3, 4\]) { params in
            Text(String(params.item1))
            Button(String(params.item2)) {}
            Text(String(params.item3))
        }
    }
}

But it won’t compile because in ViewWithList I get an error Generic parameter 'Content' could not be inferred

But it compiles if I pass all the params as plain arguments list, without a struct, but it may get really messy because there is no way to omit some specific arguments and I have to list them all in closure

import SwiftUI

struct ListTestView<T: Hashable, Content: View>: View {
    private let items: [T]
    private let listItemBuilder: (T, T, T) -> Content

    init(
        _ list: [T],
        @ViewBuilder listItem: @escaping (T, T, T) -> Content
    ) {
        self.items = list
        self.listItemBuilder = listItem
    }

    var body: some View {
        VStack {
            ForEach(filteredItems, id: \.self) { item in
                listItemBuilder(item, item, item) // ?? Text(String(item))
            }
        }
    }

    private var filteredItems: [T] {
        // ...some filtration
        return items
    }
}

struct ViewWithList: View {
    var body: some View {
        ListTestView([1, 2, 3, 4]) { item1, item2, item3 in
            Text(String(item1))
            Button(String(item2)) {}
            Text(String(item3))
        }
    }
}

Latest articles

spot_imgspot_img

Related articles

Leave a reply

Please enter your comment!
Please enter your name here

spot_imgspot_img