I am creating a reusable bottom up panel where the view displayed inside the bottom up panel will be different. I also wanted this panel to be a view modifier. I have created view modifiers in past, but hasn’t passed view as a view modifier content ever. When I try to pass the view, I am getting an error described below.
View modified code:
struct BottomPanel: ViewModifier {
@Binding var isPresented: Bool
let panelContent: Content
init(isPresented: Binding<Bool>, @ViewBuilder panelContent: @escaping () -> Content) {
self.panelContent = panelContent()
self._isPresented = isPresented
}
func body(content: Content) -> some View {
content.overlay(self.$isPresented.wrappedValue ? bottomPanelContent() : nil)
}
@ViewBuilder
private func bottomPanelContent() -> some View {
GeometryReader { geometry in
VStack(spacing: 0) {
self.panelContent
}
// some modifiers to change the color or height of the panel.
}
}
}
View extension:
extension View {
func bottomPanel(isPresented: Binding<Bool>, @ViewBuilder panelContent: @escaping () -> BottomPanel.Content) -> some View {
return modifier(BottomPanel(isPresented: isPresented, panelContent: panelContent)
}
}
Content view and child view that I wish to open in bottom up panel:
struct ContentView: View {
@State var showBottomPanel: Bool = false
var body: some View {
VStack {
Button(action: { self.showBottomPanel = true}) {
Text("Click me to open bottom panel")
}
}
.bottomPanel(isPresented: $self.showBottomPanel, panelContent: { ChildView() })
}
}
struct ChildView: View {
var body: some View {
VStack {
Button("Click Me 1", action: {}).foregroundColor(.blue)
Button("Click Me 2", action: {}).foregroundColor(.red)
}
}
}
Error: Cannot convert value of type 'ChildView' to closure result type 'BottomPanel.Content' (aka '_ViewModifier_Content<BottomPanel>')
.
What am I doing wrong? How do I pass the view to BottomPanel?
Note: I have removed a lot of code from bottom panel to keep the code post short, but let me know if it’s needed and I can share.
Thanks for reading!
2
Answers
I was able to get the content to present by making a handful of changes. Without knowing how the content is meant to look, I cannot definitively say whether or not this actually presents the content as desired – but it presents the content nonetheless, circumventing the error that you were trying to bypass
You wrote this:
The problem is that
Content
here is a special type defined by SwiftUI. It’s a type-erased container kind of likeAnyView
, except that you cannot create your own values of thisContent
type.You need to introduce your own generic type parameter for the panel content. You should also store the
ViewBuilder
callback that creates the panel content rather than computing it even when the panel isn’t shown.Then you need to update your
bottomPanel
modifier to also be generic. In this case you can use the opaque parameter syntax instead of adding an explicitPanelContent
generic parameter: