6

I'm curious if you can create a custom container view within SwiftUI. I know that you can create custom content views, such as Text(), but wasn't sure if you could replicate the functionality of say HStack { }

Something akin to:

HGrid {
    Text("Lorem ipsum")
    Text("Lorem ipsum")
}

Where the custom container view (aka HGrid) would then, say, add Spacer() between each added component. Essentially, as an example, transpiling it to:

HStack {
    Text("Lorem ipsum")
    Spacer()
    Text("Lorem ipsum")
}
dustinnewbold
  • 204
  • 3
  • 12

2 Answers2

8

SwiftUI implements this with the ViewBuilder @functionBuilder

struct HGrid <Content: View>: View {
    init(@ViewBuilder builder: () -> Content) {
        let content = builder()
        ...
    }
}

See also

Ugo Arangino
  • 2,802
  • 1
  • 18
  • 19
  • Does it make sense to just define a property like so `let content: () -> Content` instead of this custom init? – Lubiluk Jun 27 '19 at 23:01
  • You can do that. But than it is just a arbitrary function returning a view and not a ViewBuilder like in `HStack`. – Ugo Arangino Jun 27 '19 at 23:49
  • 4
    @UgoArangino Hi, I don't understand how to create custom container as HGrid using your example? How to add spacer between each added component? How are you going to modify what you get in the builder()? – Denis Vert Aug 05 '19 at 20:07
0

If we try to keep it simple and do the only thing, two options can be proposed:

import SwiftUI
// Via custom containers
struct HGrid<Content: View>: View {
    let C1: Content
    let C2: Content

    var body: some View {
        HStack {
            C1
            Spacer()
            C2
        }
    }
}
// Via View composition
struct HGridComposition: View {
    var text1: String
    var text2: String

    var body: some View {
        HStack {
            Text(text1)
            Spacer()
            Text(text2)
        }
    }
}


struct ContentView: View {
    var body: some View {
        VStack {
            Text("Composed from Views").font(.headline)
        HGrid(C1: Text("First"), C2: Text("Second"))
            Divider()
            Text("Composed from texts (String)").font(.headline)
        HGridComposition(text1: "Text One", text2: "Text Two")
        }
        .padding([.leading, .trailing])
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Paul B
  • 3,989
  • 33
  • 46