SwiftUI:扫描二维码后如何正确触发Alert弹窗?
解决SwiftUI中Sheet未关闭就弹出Alert的冲突问题
问题描述
需求:扫描二维码后显示Alert弹窗,识别正确时显示“Correct”,错误时显示“InCorrect”。
使用CodeScanner 2.3.3编写SwiftUI代码,点击按钮通过Sheet打开CodeScannerView,扫描完成后在handleScan方法中关闭Sheet并触发Alert,但运行后日志提示控制器正在presenting内容,Alert无法显示。核心原因是Sheet尚未完全关闭就尝试弹出Alert,导致控制器层级冲突。
用户代码
@Published var showingAlert = false Button(action: { showCameraForQR = true }, label: { Image ("QRCodeIcon") .resizable() .aspectRatio(contentMode: .fit) .foregroundColor(Color.gray.opacity(0.5)) }).frame(width: 30, height: 30, alignment: .center) .padding(.top,5) .padding(.bottom,15) .padding(.leading,5) .padding(.trailing,5) .sheet(isPresented: $showCameraForQR) { CodeScannerView(codeTypes: [.qr], completion: handleScan) } .alert(isPresented: $showingAlert) { Alert(title: Text(titleOfAlert)) } func handleScan(result: Result<ScanResult, ScanError>) { showCameraForQR = false // Code to set the title. titleOfAlert = "Correct" // Trigger showing of alert now showingAlert = true }
错误日志
Attempt to present <SwiftUI.PlatformAlertController: 0x104808e00> on <_TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier__: 0x102018800> (from <_TtGC7SwiftUI32NavigationStackHostingControllerVS_7AnyView_: 0x1021dae00>) which is already presenting <_TtGC7SwiftUI29PresentationHostingControllerVS_7AnyView_: 0x1022c7000>.
解决方案
方法一:利用Sheet的onDismiss回调
Sheet的关闭是异步操作,通过onDismiss回调可以在Sheet完全关闭后再触发Alert,从根源避免冲突。
修改后的代码:
@Published var showingAlert = false @Published var titleOfAlert = "" @Published var showCameraForQR = false Button(action: { showCameraForQR = true }, label: { Image ("QRCodeIcon") .resizable() .aspectRatio(contentMode: .fit) .foregroundColor(Color.gray.opacity(0.5)) }).frame(width: 30, height: 30, alignment: .center) .padding(.top,5) .padding(.bottom,15) .padding(.leading,5) .padding(.trailing,5) // 添加onDismiss回调,Sheet关闭时触发Alert .sheet(isPresented: $showCameraForQR, onDismiss: { showingAlert = true }) { CodeScannerView(codeTypes: [.qr], completion: handleScan) } .alert(isPresented: $showingAlert) { Alert(title: Text(titleOfAlert)) } func handleScan(result: Result<ScanResult, ScanError>) { showCameraForQR = false // 根据扫描结果设置Alert标题 titleOfAlert = result.isSuccess ? "Correct" : "InCorrect" }
方法二:延迟触发Alert
通过DispatchQueue.main.asyncAfter给Sheet关闭预留短暂时间,确保Sheet完全关闭后再弹出Alert。
修改后的handleScan方法:
func handleScan(result: Result<ScanResult, ScanError>) { showCameraForQR = false titleOfAlert = result.isSuccess ? "Correct" : "InCorrect" // 延迟0.2秒触发Alert,适配Sheet关闭的异步过程 DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { showingAlert = true } }
说明
两种方法中,方法一是更可靠的方案,因为它依赖Sheet关闭的官方回调,不会受设备性能差异影响;方法二则是通过延迟规避问题,虽然简单但可能在某些性能较差的设备上仍出现冲突。
内容的提问来源于stack exchange,提问作者user1300830
相关产品推荐
相关产品推荐

