SwiftUI技术问询:Sheet关闭后无法触发Alert如何解决?
问题重现
你写的这段代码逻辑上没问题,但实际运行时,点击Sheet里的"Close with alert"按钮,Sheet会关闭但Alert却不弹出,同时控制台会抛出这个警告:
[Presentation] Attempt to present <SwiftUI.PlatformAlertController: 0x7fbbab012200> on < TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier _: 0x7fbbaa60b7d0> (from < TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier _: 0x7fbbaa60b7d0>) which is already presenting < TtGC7SwiftUI22SheetHostingControllerVS_7AnyView : 0x7fbbaa413200>
问题根源
这是因为SwiftUI的Sheet关闭是带有动画的,当你设置showSheet = false后,系统并不会立即标记Sheet已完全关闭——动画还在运行的这段时间里,当前视图控制器仍然处于"正在presenting Sheet"的状态。这时候尝试present Alert,就会违反iOS的模态视图规则(一个控制器不能同时present两个模态视图),所以Alert被系统阻止显示了。
解决方案
这里有两种可靠的解决方式,你可以根据需求选择:
方案1:利用Sheet的onDismiss回调(推荐)
Sheet的onDismiss回调会在Sheet**完全关闭(动画结束)**后触发,这时候再去显示Alert就不会有冲突了。修改后的代码如下:
import SwiftUI struct ContentView: View { @State private var showSheet = false @State private var showAlert = false var body: some View { Button("Press") { showSheet = true } .sheet(isPresented: $showSheet, onDismiss: { // Sheet完全关闭后才触发Alert showAlert = true }) { Button("Close with alert") { showSheet = false } } .alert(isPresented: $showAlert) { Alert(title: Text("Alert")) } } }
这种方法是最稳妥的,因为它完全依赖系统的回调时机,不会受动画时长变化的影响。
方案2:延迟触发Alert
如果你需要在Sheet的按钮点击逻辑里直接控制(比如需要根据Sheet里的操作决定是否显示Alert),可以给Alert的触发加一个短暂延迟,等Sheet的关闭动画完成后再执行:
import SwiftUI struct ContentView: View { @State private var showSheet = false @State private var showAlert = false var body: some View { Button("Press") { showSheet = true } .sheet(isPresented: $showSheet) { Button("Close with alert") { showSheet = false // 延迟0.3秒,对应Sheet关闭动画的大致时长 DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { showAlert = true } } } .alert(isPresented: $showAlert) { Alert(title: Text("Alert")) } } }
注意:这个延迟时间是根据iOS默认的Sheet动画时长估算的,不同系统版本可能略有差异,所以方案1的可靠性更高。
内容的提问来源于stack exchange,提问作者G. Marc

