I have 2 views:
import SwiftUI
struct SwiftUIView: View {
var body: some View {
NavigationStack {
... //show SwiftUIView2
}
.toolbar {
...
}
.overlay {
... //fullscreen overlay
}
}
}
#Preview {
SwiftUIView()
}
import SwiftUI
struct SwiftUIView2: View {
var body: some View {
NavigationStack {
...
}
.toolbar {
...
}
.overlay {
... //fullscreen overlay
}
}
}
#Preview {
SwiftUIView()
}
The first overlay works as expected. The second overlay overlays everything except toolbar buttons. How to fix this issue?
Tried different ways but all of them unsuccessful. Tried .fullScreenCover(...)
but it has its own problems with animation, sequential dismissing-presenting and etc.
Example
struct OverlayView: ViewModifier {
@Binding var isPresented: Bool
let modalContent: AnyView
func body(content: Content) -> some View {
ZStack {
content
if isPresented {
modalContent
.edgesIgnoringSafeArea(.all)
}
}
}
}
extension View {
func overlayModal(isPresented: Binding<Bool>, @ViewBuilder modalContent: () -> some View) -> some View {
self.modifier(OverlayView(isPresented: isPresented, modalContent: AnyView(modalContent())))
}
}
struct ContentView: View {
@State var showModal = false
@State var showNext = false
var body: some View {
NavigationStack {
Button {
showModal = true
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
showModal = false
showNext = true
}
} label: {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
}
.padding()
}
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
NavigationLink {
EmptyView()
} label: {
Rectangle()
.frame(width: 44, height: 44)
}
}
}
.navigationDestination(isPresented: $showNext) {
ContentView()
}
}
.overlayModal(isPresented: $showModal) {
Color.black.opacity(0.5)
}
}
}
My current solution:
import SwiftUI
// Shared State for Overlay Visibility
class OverlayManager: ObservableObject {
static let shared = OverlayManager()
@AppStorage("showOverlayOnRoot") var showOverlayOnRoot = false
@AppStorage("showOverlay2OnRoot") var showOverlay2OnRoot = false
@AppStorage("showOverlay3OnRoot") var showOverlay3OnRoot = false
}
struct SwiftUIView: View {
@StateObject private var overlayManager = OverlayManager.shared
@State private var showNextView = false
var body: some View {
NavigationStack {
VStack {
Text("SwiftUIView")
NavigationLink("Go to SwiftUIView2") {
SwiftUIView2()
}
}
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
NavigationLink {
EmptyView()
} label: {
Rectangle()
.frame(width: 44, height: 44)
}
}
}
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button("Show Overlay") {
overlayManager.showOverlayOnRoot = true
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
overlayManager.showOverlayOnRoot = false
}
}
}
}
}
.overlayModal(isPresented: $overlayManager.showOverlayOnRoot) {
Color.black.opacity(0.5)
.overlay(
Text("Overlay on Root")
.foregroundColor(.white)
)
}
.overlayModal(isPresented: $overlayManager.showOverlay2OnRoot) {
Color.red.opacity(0.5)
.overlay(
Text("Overlay 2 on Root")
.foregroundColor(.white)
)
}
.overlayModal(isPresented: $overlayManager.showOverlay3OnRoot) {
Color.blue.opacity(0.5)
.overlay(
Text("Overlay 3 on Root")
.foregroundColor(.white)
)
}
}
}
struct SwiftUIView2: View {
var body: some View {
NavigationStack {
VStack {
Text("SwiftUIView2")
Button("Show Overlay 2 on Root") {
OverlayManager.shared.showOverlay2OnRoot = true
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
OverlayManager.shared.showOverlay2OnRoot = false
}
}
NavigationLink("Go to SwiftUIView3") {
SwiftUIView3()
}
}
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
NavigationLink {
EmptyView()
} label: {
Rectangle()
.frame(width: 44, height: 44)
}
}
}
}
}
}
struct SwiftUIView3: View {
var body: some View {
VStack {
Text("SwiftUIView3")
Button("Show Overlay 3 on Root") {
OverlayManager.shared.showOverlay3OnRoot = true
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
OverlayManager.shared.showOverlay3OnRoot = false
}
}
}
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
NavigationLink {
EmptyView()
} label: {
Rectangle()
.frame(width: 44, height: 44)
}
}
}
}
}
#Preview {
SwiftUIView()
}
The idea - I place each fullscreen overlay into the root view (where it still can overlay toolbar). It is not convenient but at least works from any part of the app, allows different overlays and there is no need to hide-show navigation bar. Maybe someone could suggest better.