5

I'm trying to create a popup menu, similar to .menu() modifier that we have in SwiftUI. I managed to create the modifier and it is working fine.

Problem: The issue I have is that I am unable to have an overlay extend to the entire screen when the menu is displayed. Here's what I get when I try to overlay it (show in blue)

The reason for the problem is that I've a TabView which is wrapped in a parent NavigationView.

enter image description here

Menu modifier: Here's the modifier that I've created to show popup menus.

import SwiftUI

struct PopupMenu<MenuContent: View>: ViewModifier {
    
    let menuContent: MenuContent
    @Binding var isPresented: Bool
    
    // Number crunching to limit menu size to 2/3rd of the screen
    private let paddingRatio: CGFloat = 0.33
    private let screenWidth = UIScreen.main.bounds.width
    
    init(isPresented: Binding<Bool>, @ViewBuilder content: () -> MenuContent) {
        _isPresented = isPresented
        menuContent = content()
    }
    
    func body(content: Content) -> some View {
        ZStack {
            // The content to show menu on
            content
            if isPresented {
                ZStack {
                    // Overlay to hide background - blue color is just to accentuate the issue
                    Color.blue
                        .onTapGesture {
                            isPresented.toggle()
                        }
                    // Actual menu rectangle
                    VStack {
                        HStack() {
                            Spacer(minLength: paddingRatio * screenWidth)
                            menuContent
                                .padding(regularPadding)
                                .background(
                                    RoundedRectangle(cornerRadius: regularCorner)
                                        .foregroundColor(.white)
                                        .shadow(color: darkGray.opacity(0.3), radius: 24, x: -4, y: 12)
                                )
                        }
                        Spacer()
                    }
                    .padding(.trailing, regularPadding)
                    .padding(.top, 2)
                }
            }
        }
    }
}

extension View {
    func popupMenu<MenuContent: View>(isPresented: Binding<Bool>, @ViewBuilder content: @escaping () -> MenuContent) -> some View {
        self.modifier(PopupMenu(isPresented: isPresented, content: content))
    }
}
Siddharth Kamaria
  • 2,448
  • 2
  • 17
  • 37

1 Answers1

3

So it's apparent that you have a NavigationView. So in that case, use .overlay(popupMenu()) to overlay the menu over the existing NavigationView

for example,

NavigationView {
    //Some content
}
.overlay(popup(_,_))

and in you popOver View, make sure to add the .ignoresSafeArea() to the Color view as follows

Color.blue
   .ignoresSafeArea()
   .onTapGesture {
       isPresented.toggle()
    }

Visal Rajapakse
  • 1,718
  • 1
  • 9
  • 15
  • That is a bit tricky since each tab has it's own menu and putting an overlay in the parent view means communicating from all child views on what to show - which is something I want to avoid! – Siddharth Kamaria Jul 08 '21 at 15:10
  • 1
    answered here https://stackoverflow.com/a/69342575/10229223 check this out! – amin torabi Sep 27 '21 at 07:23
  • @amintorabi That seems interesting. Although the problem is solved for now, but I'll surely give it a try! – Siddharth Kamaria Sep 27 '21 at 14:46
  • @SiddharthKamaria how did you solved that – Ajay Singh Mehra Nov 17 '22 at 04:55
  • @AjaySinghMehra I didn't. Back then, I kept the modifier on the top-most view and communicated with the child views using `Binding`s. It was tedious but it worked. Now that we have `.presentationDetents([.medium])` in iOS 16+ it is easier to create overlays of whatever size that you need. – Siddharth Kamaria Nov 17 '22 at 10:58