Toast is hidden by Popover
Hi, I want to use a toast to display error messages in my app (The toast should be at the bottom of the screen). However i noticed that when using a native .popover screen, the toast is below the popover and can't be seen. I tried using the .isOpaque(true) modifier, however that just closes the popover, which is not what I want. Is there any way to fix this? Thanks in advance.
Hey @saff1x, opaque popup is just a fullscreen cover, I can't control what it covers. Apple also doesn't give me any way to place any custom views above the system views like sheets, popovers and navbar, so no control there either. If you know of a way to bypass that restriction, you are most welcome to commit a PR. Have a nice day
I found a workaround based on this article: https://www.fivestars.blog/articles/swiftui-windows/
Not really sure what its doing and its probably not the best solution but it works for me. Hope it helps
import UIKit
import SwiftUI
import ExytePopupView
class SnackbarManager: ObservableObject{
@Published var showSnackbar: Bool = false
@Published var snackbarMessage: String = ""
static let shared = SnackbarManager()
private init() {}
func showSnackbar(message: String) {
snackbarMessage = message
showSnackbar = true
}
func dismissSnackbar() {
showSnackbar = false
}
}
struct SnackbarViewModifier: ViewModifier {
@ObservedObject var snackbarManager = SnackbarManager.shared
func body(content: Content) -> some View {
content
.popup(
isPresented: Binding(
get: { snackbarManager.showSnackbar },
set: { value in
snackbarManager.showSnackbar = value
}
),
view: {
Snackbar(message: snackbarManager.snackbarMessage)
},
customize: {
$0
.type(.toast)
.position(.bottom)
.autohideIn(4)
.dragToDismiss(true)
.useKeyboardSafeArea(true)
}
)
}
}
final class SceneDelegate: NSObject, UIWindowSceneDelegate {
var secondaryWindow: UIWindow?
func scene(_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions) {
if let windowScene = scene as? UIWindowScene {
setupSecondaryOverlayWindow(in: windowScene)
}
}
func setupSecondaryOverlayWindow(in scene: UIWindowScene) {
let secondaryViewController = UIHostingController(
rootView:
EmptyView()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.modifier(SnackbarViewModifier())
)
secondaryViewController.view.backgroundColor = .clear
let secondaryWindow = PassThroughWindow(windowScene: scene)
secondaryWindow.rootViewController = secondaryViewController
secondaryWindow.isHidden = false
self.secondaryWindow = secondaryWindow
}
}
class PassThroughWindow: UIWindow {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
guard let hitView = super.hitTest(point, with: event) else { return nil }
return rootViewController?.view == hitView ? nil : hitView
}
}
And then added this in the iosApp file:
import SwiftUI
@main
struct iosApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { return true }
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
let configuration = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
if connectingSceneSession.role == .windowApplication {
configuration.delegateClass = SceneDelegate.self
}
return configuration
}
}
@saff1x are you able to use buttons within your toast? I am using the same approach, however I am unable to get Buttons. I think the PassThroughWindow is intercepting the touches. Wondering how you solved this problem.
I do get the PassThroughWindow to successfully register taps on the screen both in the toast as well as other parts of the view. But the taps do not make it through to the toast.
Hey guys, in version 4.0.0, I added .window display type for a popup, does this by any chance solve your issue? have a good day!