You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI如何通过UIWindow展示位于所有视图上层的类Toast自定义视图

SwiftUI 实现全局顶层Toast的解决方案

你当前的代码存在两个核心问题:未给UIHostingController的视图设置布局约束导致位置异常,以及未关闭hosting视图的交互拦截会影响下层控件使用。按照以下步骤修改即可满足需求:

核心修改逻辑

  • 关闭hosting视图的用户交互,让所有触摸事件透传到下层视图,保证应用正常交互
  • 给hosting视图设置透明背景和自动布局约束,固定Toast的展示位置
  • 静态持有当前展示的UIHostingController实例,避免实例被提前释放导致Toast消失
  • 补充自动消失和重复弹出处理逻辑,避免内存泄漏和多Toast重叠

完整实现代码

public struct ToastView: View {
    // 可自定义参数
    let message: String
    let duration: TimeInterval = 2.0
    // 持有当前显示的Toast实例,避免被提前释放
    private static var currentHosting: UIHostingController<ToastView>?
    
    static func displayToastAboveAll(message: String) {
        // 兼容多场景的keyWindow获取逻辑,适配iOS13+多窗口特性
        guard let window = UIApplication.shared.connectedScenes
                .filter({$0.activationState == .foregroundActive})
                .compactMap({$0 as? UIWindowScene})
                .first?.windows
                .filter({$0.isKeyWindow}).first else { return }
        
        // 重复弹出时先移除已存在的Toast
        if let currentHosting = currentHosting {
            currentHosting.view.removeFromSuperview()
            self.currentHosting = nil
        }
        
        let toastView = ToastView(message: message)
        let hostingController = UIHostingController(rootView: toastView)
        // 持有当前实例
        currentHosting = hostingController
        
        // 核心配置
        hostingController.view.backgroundColor = .clear // 清除默认白色背景
        hostingController.view.isUserInteractionEnabled = false // 禁用交互拦截,透传事件到下层
        hostingController.view.translatesAutoresizingMaskIntoConstraints = false
        
        window.addSubview(hostingController.view)
        
        // 布局约束,可根据需求调整位置
        NSLayoutConstraint.activate([
            hostingController.view.centerXAnchor.constraint(equalTo: window.centerXAnchor),
            // 底部距离安全区20pt,如需顶部展示可替换为topAnchor
            hostingController.view.bottomAnchor.constraint(equalTo: window.safeAreaLayoutGuide.bottomAnchor, constant: -20),
            // 限制最大宽度,避免文字过长撑满屏幕
            hostingController.view.widthAnchor.constraint(lessThanOrEqualTo: window.widthAnchor, constant: -40)
        ])
        
        // 添加入场、退场动画
        hostingController.view.alpha = 0
        UIView.animate(withDuration: 0.3) {
            hostingController.view.alpha = 1
        } completion: { _ in
            UIView.animate(withDuration: 0.3, delay: toastView.duration) {
                hostingController.view.alpha = 0
            } completion: { _ in
                hostingController.view.removeFromSuperview()
                // 释放实例,避免内存泄漏
                currentHosting = nil
            }
        }
    }

    public var body: some View {
        // 可自定义Toast样式
        Text(message)
            .padding(.horizontal, 16)
            .padding(.vertical, 12)
            .background(Color.black.opacity(0.7))
            .foregroundColor(.white)
            .cornerRadius(8)
    }
}

使用方式

直接调用静态方法即可全局弹出:

ToastView.displayToastAboveAll(message: "这是一个Toast提示")

内容的提问来源于stack exchange,提问作者Froehly Fabrice

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 14:48:04