Skip to content

Instantly share code, notes, and snippets.

@michael94ellis
Last active June 11, 2025 11:10
Show Gist options
  • Save michael94ellis/2b2fe959bf2416a7a61140602ebed0cf to your computer and use it in GitHub Desktop.
Save michael94ellis/2b2fe959bf2416a7a61140602ebed0cf to your computer and use it in GitHub Desktop.
A simple way to show a toast in SwiftUI apps over all other view activity.
// Inspired by Federico Zanetello
// https://www.fivestars.blog/articles/swiftui-windows/
import UIKit
import SwiftUI
struct ToastView: View {
let message: String
/// Total duration that the toast will be visible on screen
let duration: TimeInterval
/// Duration of the movement in and out
let animationDuration: TimeInterval
/// Opacity control
@State private var isVisible = false
/// Start position
@State private var offsetY: CGFloat = 50
var body: some View {
VStack {
Spacer()
Text(message)
.padding()
.background(Color.red.opacity(0.8))
.foregroundColor(.white)
.cornerRadius(8)
.opacity(isVisible ? 1 : 0)
.offset(y: offsetY)
.onAppear {
withAnimation(.easeInOut(duration: animationDuration)) {
offsetY = -50
isVisible = true
}
DispatchQueue.main.asyncAfter(deadline: .now() + (duration - animationDuration)) {
withAnimation(.easeInOut(duration: animationDuration)) {
offsetY = 50 // Moves back down
isVisible = false
}
}
}
}
}
}
final class Toaster {
static let shared = Toaster()
private var toastWindow: UIWindow?
var activeWindow: UIWindow? {
UIApplication.shared.connectedScenes
.compactMap { $0 as? UIWindowScene }
.flatMap { $0.windows }
.first { $0.isKeyWindow }
}
func showToast(message: String, duration: TimeInterval = 2.0) {
if let window = activeWindow,
let scene = window.windowScene {
if toastWindow == nil {
let toastWindow = PassThroughWindow(windowScene: scene)
toastWindow.windowLevel = .alert + 1
toastWindow.backgroundColor = UIColor.clear
self.toastWindow = toastWindow
}
let toastView = ToastView(message: message,
duration: duration,
animationDuration: 0.3)
let toastViewController = UIHostingController(rootView: toastView)
toastViewController.view.backgroundColor = UIColor.clear
toastWindow?.rootViewController = toastViewController
toastWindow?.isHidden = false
DispatchQueue.main.asyncAfter(deadline: .now() + duration) {
self.toastWindow?.isHidden = true
}
}
}
}
class PassThroughWindow: UIWindow {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// Get view from superclass.
guard let hitView = super.hitTest(point, with: event) else { return nil }
// If the returned view is the `UIHostingController`'s view, ignore.
return rootViewController?.view == hitView ? nil : hitView
}
}
@michael94ellis
Copy link
Author

A Demo of this code

ezgif-2382a0170c7581

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment