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

SwiftUI:如何在无Sheet等弹窗时触发Alert提示?

SwiftUI中检测无弹窗状态再触发应用激活Alert的解决方案

问题场景

在SwiftUI中实现应用重新激活时弹出Alert的功能,主视图状态下运行正常,但当Sheet展示时将应用切至后台再唤起,Alert无法弹出且报错,此后甚至通过按钮也无法触发Alert。核心需求是:当应用激活且当前无任何弹窗(如Sheet)展示时,才触发Alert,即需判断if itsOkToShowAlert { showAlert = true }。

原代码

import SwiftUI

struct ContentView: View {
    @State private var showAlert = false
    @State private var showingSheet = false
    @Environment(\.scenePhase) var scenePhase
    
    var body: some View {
        VStack {
            Text("Hello, world!").padding()
            Button("Show sheet") {
                showingSheet = true
            }.padding()
            Button("Show alert") {
                showAlert = true
            }.padding()
        }
        .sheet(isPresented: $showingSheet) {
            VStack {
                Spacer()
                Text("HELLO SHEET!").font(.largeTitle)
                Spacer()
            }
        }
        .task(id: scenePhase) {
            if scenePhase == .active {
                print("Activated")
                showAlert = true
            }
        }
        .alert("HAVE AN ALERT", isPresented: $showAlert) {
            let _ = print("Showing alert")
            Button("OK", role: .cancel) {
                print("alert button pressed")
            }
        }
        .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

触发报错

2023-05-27 17:18:50.217767-0700 testalert[44589:31594186] [Presentation] Attempt to present <SwiftUI.PlatformAlertController: 0x16282f000> on <_TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier__: 0x162819400> (from <_TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier__: 0x162819400>) which is already presenting

解决方案

核心思路

SwiftUI中无法直接获取全局的弹窗展示状态,但所有自定义的模态视图(如Sheet、FullScreenCover等)都是通过状态变量控制显示的,因此可以通过检查这些状态变量是否都处于未激活状态,来判断当前是否可以弹出Alert。

修改步骤

  1. 新增判断变量,聚合所有自定义模态的状态,确认无弹窗展示
  2. 在应用激活触发Alert、按钮触发Alert时,先检查该判断条件,只有符合要求才设置showAlert = true,避免状态异常

修改后的代码

import SwiftUI

struct ContentView: View {
    @State private var showAlert = false
    @State private var showingSheet = false
    @Environment(\.scenePhase) var scenePhase
    
    // 判断是否允许展示Alert:无Sheet且Alert未在展示
    private var itsOkToShowAlert: Bool {
        !showingSheet && !showAlert
    }
    
    var body: some View {
        VStack {
            Text("Hello, world!").padding()
            Button("Show sheet") {
                showingSheet = true
            }.padding()
            Button("Show alert") {
                if itsOkToShowAlert { // 按钮触发时做状态检查
                    showAlert = true
                }
            }.padding()
        }
        .sheet(isPresented: $showingSheet) {
            VStack {
                Spacer()
                Text("HELLO SHEET!").font(.largeTitle)
                Spacer()
            }
        }
        .task(id: scenePhase) {
            if scenePhase == .active {
                print("Activated")
                if itsOkToShowAlert { // 应用激活时检查状态
                    showAlert = true
                }
            }
        }
        .alert("HAVE AN ALERT", isPresented: $showAlert) {
            let _ = print("Showing alert")
            Button("OK", role: .cancel) {
                print("alert button pressed")
            }
        }
        .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

扩展说明

如果有多个自定义模态视图(如多个Sheet、FullScreenCover),只需扩展itsOkToShowAlert的判断条件,加入所有模态的状态变量即可:

private var itsOkToShowAlert: Bool {
    !showingSheet && !showingAnotherSheet && !showingFullScreenCover && !showAlert
}

对于系统级弹窗(如权限请求、系统Alert),无法通过自定义状态变量检测,这种场景下建议在触发Alert前增加延迟,或者通过捕获系统通知来判断,但这类场景属于特殊情况,大部分业务场景下通过自定义状态变量即可覆盖需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 08:45:00