I have an interface where I want to be able to toggle a Side Menu and also a toolbar. The Side Menu slides in/out from the right and the Toolbar from the bottom.
I'm making it generic so it's reusable to whatever content one wants to use.
The code is this:
struct ControlsOverlayView<ToolbarContent: View, MenuContent: View>: View {
let menuWidth: CGFloat
let isMenuActive: Bool
let onMenuHide: () -> Void
let menuContent: MenuContent
let toolbarHeight: CGFloat
let isToolbarActive: Bool
let onToolbarHide: () -> Void
let toolbarContent: ToolbarContent
init(menuWidth: CGFloat = 270,
isMenuActive: Bool = true,
onMenuHide: @escaping () -> Void,
toolbarHeight: CGFloat = 44,
isToolbarActive: Bool = true,
onToolbarHide: @escaping () -> Void,
@ViewBuilder menuContent: () -> MenuContent,
@ViewBuilder toolbarContent: () -> ToolbarContent) {
self.menuWidth = menuWidth
self.isMenuActive = isMenuActive
self.onMenuHide = onMenuHide
self.menuContent = menuContent()
self.toolbarHeight = toolbarHeight
self.isToolbarActive = isToolbarActive
self.onToolbarHide = onToolbarHide
self.toolbarContent = toolbarContent()
}
var body: some View {
ZStack {
GeometryReader { _ in
EmptyView()
}
.background(Color.gray.opacity(0.3))
.opacity(self.isMenuActive ? 1.0 : 0.0)
.animation(Animation.easeIn.delay(0.25))
.onTapGesture {
self.onMenuHide()
}
GeometryReader { geometry in
VStack {
HStack {
Spacer()
let space: CGFloat = 0.0
let offset = self.isMenuActive ? space : space + self.menuWidth
let toolbarHeight = isToolbarActive ? self.toolbarHeight : 0
self.menuContent
.frame(width: self.menuWidth, height: geometry.size.height - toolbarHeight, alignment: .center)
.background(Color.red)
.offset(x: offset)
.animation(.default)
}
let offset = self.isToolbarActive ? 0 : -self.toolbarHeight
self.toolbarContent
.frame(width: geometry.size.width,
height: self.toolbarHeight,
alignment: .center)
.background(Color.yellow)
.offset(y: offset)
.animation(.default)
}
}
}
}
}
struct ControlsOverlayView_Previews: PreviewProvider {
static var previews: some View {
ControlsOverlayView(menuWidth: 270,
isMenuActive: true,
onMenuHide: {},
toolbarHeight: 44,
isToolbarActive: false,
onToolbarHide: {}) {
Text("Menu Content")
} toolbarContent: {
Text("Toolbar Content")
}
}
}
The Problem: Given the preview settings, I don't think I should see the toolbar. And yet I do. Attached is a screenshot of the Canvas with the toolbarContent.frame(...) line highlighted in code. You can see that it shows a frame to be drawn offscreen, but the content is not drawn there with it.
I was following from the code to make the side menu slide in/out on the horizontal axis and thought I just need to do essentially the same for the toolbar, but as you can see, that approach doesn't work.
So I made it work, but I still have no idea why.
Using this new body code, I set the frame of the HStack, and adjusted the offset. Strange is that given the offset value of 'self.toolbarHeight / 2' I don't think it should yield the desired result, and yet it does: