Show Black Layer Like Sheet or AlertView over all Content

42 views Asked by At

I have a Hstack that contains SideBar() and a Group that displays the contents based on the selection of the SideBar.

struct Main: View {
    @State private var currentScreen: AppScreen? = .analisys

    var body: some View {
        HStack(spacing: 0) {
            SideBar()
            Group {
                if let currentScreen {
                    currentScreen.content
                }
            }
    }
        
        @ViewBuilder
        private func SideBar() -> some View {
            // The SideBar Menu
        }
}

Content is managed via an enum called AppScreen

import SwiftUI

enum AppScreen: CaseIterable, Identifiable {
    case analisys, trends
    var id: AppScreen { self }
        
   @ViewBuilder
    var content: some View {
        switch self {
        case .analisys: ContentUnavailableView(name + " Non disponibile", systemImage: icon)
        case .trends: TrendsView()       
        }
    }
}

Inside the content called TrendView there is a button that should show a black Layer that I use for the presentation background of a customized view, as an alertView does for example. (black Layer and the alertView above it).

Now my problem is that if I call the custom view inside TrendView() using .overlay { } , the SideBar is not obscured by the black layer. How can I recreate the effect of black layer and overlapping view like alertView does in my case?

struct TrendView: View {   
    @State private var showBlackLayer: Bool = false

    var body: some View {
       
        ZStack {
            Button("Show Layer and View") {
                showBlackLayer.toggle()
            }
            .overlay {
                Color.black
                
                    .opacity(showBlackLayer ? 0.5 : 0)
                    .edgesIgnoringSafeArea(.all)
                    .onTapGesture {
                        showBlackLayer = false // Chiudi la vista nera
                    }
            }
        }
        .buttonStyle(.borderless)
    }
}
1

There are 1 answers

0
Benzy Neez On

I would suggest you use a ZStack in Main, instead of an HStack. This way, the underlying views are able to use the full screen for showing an overlay. Use leading padding on the underlying views to allow the sidebar to be seen too.

In TrendView, the ZStack needs to occupy the full screen and the overlay should then be applied to the ZStack, not to the Button. It is also better to use .ignoresSafeArea instead of .edgesIgnoringSafeArea (which is deprecated).

Here is the updated code:

struct Main: View {

    // other content as before

    var body: some View {
        ZStack(alignment: .leading) { // <- ZStack instead of HStack
            SideBar()
            Group {
                if let currentScreen {
                    currentScreen.content
                }
            }
        }
    }
}

struct TrendView: View {
    @State private var showBlackLayer: Bool = false

    var body: some View {
        ZStack {
            Button("Show Layer and View") {
                showBlackLayer.toggle()
            }
        }
        .padding(.leading, 100) // <- should correspond to width of sidebar
        .frame(maxWidth: .infinity, maxHeight: .infinity) // <- occupy full screen
        .buttonStyle(.borderless)
        .overlay { // <- moved to ZStack (and revised)
            if showBlackLayer {
                Color.black
                    .opacity(0.5)
                    .ignoresSafeArea()
                    .onTapGesture {
                        showBlackLayer = false // Chiudi la vista nera
                    }
            }
        }
    }
}

Screenshots