I’m working on an iOS app that is mostly UIKit, but I’m adding new screens using SwiftUI. I want to show a toast/banner message above everything—including the navigation bar—when a user performs an action on a SwiftUI screen. In UIKit, I would add a subview to the UIWindow or the topmost UIViewController’s view to achieve this, and I could control whether the underlying view is interactable.
In SwiftUI, I tried using a custom ViewModifier with .fullScreenCover to present my toast, but this always blocks interaction with the underlying view (like a modal), and sometimes doesn’t overlay the navigation bar as expected.
Here’s a simplified version of my code:
// ToastView: A simple toast UI component
struct ToastView: View {
let title: String
var body: some View {
Text(title)
.padding()
.background(Color.blue)
.clipShape(Capsule())
}
}
// ToastPresenter: ViewModifier to present ToastView using fullScreenCover
// The intention is to show a toast over the entire screen, including the navigation bar,
// but keep the underlying view interactable while the toast is visible.
struct ToastPresenter: ViewModifier {
@Binding var isPresented: Bool
func body(content: Content) -> some View {
content
// PROBLEM: fullScreenCover presents above the entire view hierarchy,
// but in SwiftUI NavigationStack, it appears above the presented view,
// not above the navigation bar. Also, the underlying view is always blocked.
.fullScreenCover(isPresented: $isPresented) {
ZStack(alignment: .top) {
Color.clear // Transparent background
ToastView(title: "Hello World")
}
// The toast does overlay the navigation bar as expected but underlying
// views are not interactable. This is the main issue.
}
}
}
// Extension to easily apply the ToastPresenter modifier
extension View {
func message(isPresented: Binding<Bool>) -> some View {
self.modifier(ToastPresenter(isPresented: isPresented))
}
}
// My app is on UIkit, and I am adding new screens in SwiftUI and want to show toast on it.
// Consider this view as UIKit ViewController and the SwiftUI view as a new screen in the app.
struct ContentView: View {
var body: some View {
NavigationStack {
NavigationLink("Inner Screen") {
ScreenOne()
}
}
.padding()
}
}
// ScreenOne: Example screen to trigger the toast
struct ScreenOne: View {
@State private var isPresented: Bool = false
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Button {
isPresented = true // Show the toast
} label: {
Text("Show the message")
}
}
.message(isPresented: $isPresented)
}
}
// Preview for SwiftUI canvas
#Preview {
ContentView()
}
What I want:
- Show a toast/banner above the navigation bar (like a global overlay).
- Optionally allow the underlying view to remain interactable (non-blocking), or block it if needed.
- Mimic the UIKit pattern of adding a subview to the window, but in SwiftUI.
What I’ve tried:
- .fullScreenCover (blocks interaction, acts like a modal, not what I want)
- .sheet (same issue)
- ZStack overlays (don’t cover the navigation bar if placed inside NavigationStack)
Question: How can I present a toast/banner in SwiftUI above the navigation bar, similar to adding a subview to UIWindow in UIKit, and control whether the underlying view is interactable?
Any best practices or workarounds for this scenario in a mixed UIKit/SwiftUI app would be appreciated!


UIWindowshould still work with SwiftUI.