13

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?

1 Answer 1

27

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")
            }
        }
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.