Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI view with multiple ViewBuilder

Tags:

swiftui

I have a view that represents a row in a cell that looks like this

view

This works well but the three horizontal elements (Image, Title/Subtitle, Image) are hardcoded to their respective types.

I would like to have a generic ThreeItemView that can takes 3 Views of any type and arrange them as shown. This would allow me to reuse the same container layout with any other views types.

I created a view that takes three @ViewBuilders:

import Foundation
import SwiftUI

struct ThreeItemView<Start: View, Main: View, End: View>: View {

    let start: () -> Start
    let main: () -> Main
    let end: () -> End

    init(@ViewBuilder start: @escaping() -> Start,
        @ViewBuilder main: @escaping() -> Main,
        @ViewBuilder end: @escaping() -> End) {
        self.start = start
        self.main = main
        self.end = end
    }

    var body: some View {
        return HStack {
            start()
            main()
                .frame(minWidth: 0, maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
            end()
        }
        .frame(minWidth: 0, maxWidth: .infinity, maxHeight: 60, alignment: .leading)
    }
}

struct ThreeItemContainer_Previews: PreviewProvider {
    static var previews: some View {

        ThreeItemView(start: {
            Image(systemName: "envelope.fill")
        }, main: {
            Text("Main")
        }, end: {
            Image(systemName: "chevron.right")
        })
    }
}

This works as expected but the API is a bit .. cumbersome. What would be a way to make the usage of the ThreeItemView easier?

like image 816
Jan Avatar asked Feb 25 '20 20:02

Jan


People also ask

Why use@ ViewBuilder?

The @ViewBuilder attribute is one of the few result builders available for you to use in SwiftUI. You typically use it to create child views for a specific SwiftUI view in a readable way without having to use any return keywords.

How does ViewBuilder work?

@ViewBuilder is a kind of result builder that's specifically designed to help create child views. Result builders create functions that build a result from a sequence of elements. SwiftUI uses this in its own native views, controls and components. It also uses this in the body to compose your views.

What is the maximum number of parameters allowed with the SwiftUI ViewBuilder?

Save this question. Show activity on this post. In the source code, ViewBuilder max parameters was limited to 10.


1 Answers

If you mean notation like the following

ThreeItemView {
    Start {
        Image(systemName: "envelope.fill")
    }
    Main {
        Text("Main")
    }
    End {
        Image(systemName: "chevron.right")
    }
}

then find below modified your module

typealias Start<V> = Group<V> where V:View
typealias Main<V> = Group<V> where V:View
typealias End<V> = Group<V> where V:View

struct ThreeItemView<V1, V2, V3>: View where V1: View, V2: View, V3: View {

    private let content: () -> TupleView<(Start<V1>, Main<V2>, End<V3>)>

    init(@ViewBuilder _ content: @escaping () -> TupleView<(Start<V1>, Main<V2>, End<V3>)>) {
        self.content = content
    }

    var body: some View {
        let (start, main, end) = self.content().value
        return HStack {
            start
            main
                .frame(minWidth: 0, maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
            end
        }
        .frame(minWidth: 0, maxWidth: .infinity, maxHeight: 60, alignment: .leading)
    }
}

struct ThreeItemContainer_Previews: PreviewProvider {
    static var previews: some View {

        ThreeItemView {
            Start {
                Image(systemName: "envelope.fill")
            }
            Main {
                Text("Main")
            }
            End {
                Image(systemName: "chevron.right")
            }
        }
    }
}
like image 154
Asperi Avatar answered Oct 18 '22 04:10

Asperi