Optional @ViewBuilder closure
@JoeBayLD asked:
How would you do this if the topContent and bottomContent are different view types? I made a new generic property but when using the default 'nil' argument, any callers can't infer the content type
You can make both ViewBuilder parameters non-optional, and then handle the "no bottom content" case by making an extension where BottomContent == EmptyView
:
struct TopAndBottomView<TopContent: View, BottomContent: View>: View {
let topContent: TopContent
let bottomContent: BottomContent
init(@ViewBuilder topContent: () -> TopContent,
@ViewBuilder bottomContent: () -> BottomContent) {
self.topContent = topContent()
self.bottomContent = bottomContent()
}
var body: some View {
VStack {
topContent
Spacer()
bottomContent
}
}
}
extension TopAndBottomView where BottomContent == EmptyView {
init(@ViewBuilder topContent: () -> TopContent) {
self.init(topContent: topContent, bottomContent: { EmptyView() })
}
}
// usage
TopAndBottomView(topContent: { Text("hello") })
TopAndBottomView(topContent: { Text("hello") }, bottomContent: { Text("world") })
Taking into account buildIf
feature of ViewBuilder
the following approach is possible that allows to keep ViewBuilder
in init
(that is preferable)
Tested & works with Xcode 11.2 / iOS 13.2
struct TopAndBottomView<Content>: View where Content: View {
let topContent: () -> Content
let bottomContent: () -> Content?
init(@ViewBuilder topContent: @escaping () -> Content,
@ViewBuilder bottomContent: @escaping () -> Content? = { nil }) {
self.topContent = topContent
self.bottomContent = bottomContent
}
var body: some View {
VStack {
topContent()
Spacer()
bottomContent()
}
}
}
So works as this one
struct TopAndBottomView_Previews: PreviewProvider {
static var previews: some View {
TopAndBottomView(topContent: {
Text("TOP")
}, bottomContent: {
Text("BOTTOM")
})
}
}
and this one
struct TopAndBottomView_Previews: PreviewProvider {
static var previews: some View {
TopAndBottomView(topContent: {
Text("TOP")
})
}
}