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。
修改步骤
- 新增判断变量,聚合所有自定义模态的状态,确认无弹窗展示
- 在应用激活触发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
相关产品推荐
相关产品推荐

