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")
        })
    }
}

Tags:

Ios

Swift

Swiftui