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

SwiftUI中使用ObservableObject结合Binding导致Sheet崩溃问题

Sheet连续展示导致视图崩溃问题

点击主视图按钮弹出第一个Sheet,关闭第一个Sheet后需弹出第二个Sheet,但使用ObservableObject配合Binding时,弹出第二个Sheet后视图崩溃。

相关代码

Root:

class Benef: ObservableObject {
    @Published var val: String = ""
}

@main
struct TestApp: App {
    @StateObject var beenf = Benef()
    var body: some Scene {
        WindowGroup {
            ContentView(testt: $beenf.val)
            
        }
    }
}

ContentView:

enum Test: Identifiable {
    case one
    case two
    
    var id: String {
        switch self {
        case .one : return "One"
        case .two : return "Two"
        }
    }
}

struct ContentView: View {
    @Environment(\.dismiss) private var dismiss
    @State private var showingFirst: Test?
    @State private var showingSecond: Test?
    @Binding var testt: String

    var body: some View {
        VStack {
            Button("Show First Sheet") {
                showingFirst = .one
            }
        }
        .sheet(item: $showingFirst) { tt in
            FirstSheetiew {
                showingSecond = .two
            }
        }
        .sheet(item: $showingSecond) { tt in
            VStack {
                Text("Second Sheet")
                Button("Dismiss") {
                    dismiss()
                }
            }
        }
    }
}

struct FirstSheetiew: View {
    @Environment(\.dismiss) private var dismiss

    let action: () -> Void
    
    var body: some View {
        VStack {
            Text("First Sheet")
            Button("Dismiss") {
                action()
                dismiss()
            }
        }
    }
    
}

崩溃错误信息

应用尝试以模态方式展示视图控制器 <TtGC7SwiftUI29PresentationHostingControllerVS_7AnyView>: 0x12a01b600>,但该视图控制器已被 <TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier_: 0x12a814e00> 展示。

解决方案

崩溃原因

在第一个Sheet的dismiss()执行前就设置了showingSecond = .two,此时第一个Sheet的 dismiss 流程尚未完成,主视图同时触发两个Sheet的展示操作,导致视图控制器层级冲突。

修复方法

方法一:利用Sheet的onDismiss回调(推荐)

将第二个Sheet的触发逻辑移到第一个Sheet的onDismiss回调中,确保第一个Sheet完全关闭后再展示第二个:

修改ContentView中第一个Sheet的代码:

.sheet(item: $showingFirst, onDismiss: {
    // 第一个Sheet关闭后自动弹出第二个
    showingSecond = .two
}) { tt in
    // 移除action参数,FirstSheet只需处理自身关闭
    FirstSheetiew()
}

同时简化FirstSheetiew:

struct FirstSheetiew: View {
    @Environment(\.dismiss) private var dismiss
    
    var body: some View {
        VStack {
            Text("First Sheet")
            Button("Dismiss") {
                dismiss()
            }
        }
    }
}

方法二:延迟触发第二个Sheet

如果需要在第一个Sheet的按钮点击时执行额外逻辑,可通过DispatchQueue.main.async延迟触发第二个Sheet的展示,确保dismiss流程完成:

修改FirstSheetiew的按钮动作:

Button("Dismiss") {
    dismiss()
    // 延迟执行,等待dismiss流程结束
    DispatchQueue.main.async {
        action()
    }
}

额外优化

第二个Sheet中可单独声明自身的dismiss环境变量,避免依赖主视图的dismiss,逻辑更清晰:

.sheet(item: $showingSecond) { tt in
    VStack {
        Text("Second Sheet")
        Button("Dismiss") {
            @Environment(\.dismiss) var sheetDismiss
            sheetDismiss()
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 00:05:32