You need to enable JavaScript to run this app. SwiftUI如何基于外部单例ObservableObject的属性控制sheet弹出
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI如何基于外部单例ObservableObject的属性控制sheet弹出

问题原因&解决方案

1. 解决sheet参数报错问题

sheet(isPresented:)要求传入的是Binding<Bool>类型,你直接传!authenticator.isSignedIn是普通布尔值,不符合类型要求。要获取@ObservedObject、@EnvironmentObject包装的ObservableObject中属性的绑定引用,需要加$前缀,你需要的取反逻辑可以通过自定义Binding实现:

.sheet(isPresented: Binding(
    get: { !authenticator.isSignedIn },
    set: { _ in }
)) {
    AuthenticationView()
}

如果sheet关闭后不需要修改isSignedIn的值,set块留空即可;如果关闭时需要同步状态,把对应逻辑写到set块里即可。

2. 解决操作后UI不实时更新的问题

你当前代码有两处不符合SwiftUI监听规则的写法:

  • 视图内部判断登录状态用了Authenticator.shared.isSignedIn,而不是你用@ObservedObject修饰的authenticator属性。SwiftUI只会监听被对应属性包装器修饰的实例的属性变化,直接访问全局单例的属性,视图收不到更新通知。
  • 按钮点击的signout调用也用了Authenticator.shared,统一换成你声明的authenticator实例即可。

修改后的视图代码如下:

struct AccountView: View {
    // 这里绑定的就是单例实例,可以正常监听@Published属性变化
    @ObservedObject var authenticator = Authenticator.shared
    
    var body: some View {
        NavigationView {
            ZStack {
                Color.white
                    .edgesIgnoringSafeArea(.all)
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
           
                // 换成你声明的authenticator的属性
                if authenticator.isSignedIn {
                    Button {
                        // 也用authenticator实例调用方法
                        authenticator.signout() 
                    } label: {
                        Text("Sign out")
                    }
                }
            }
            .sheet(isPresented: Binding(
                get: { !authenticator.isSignedIn },
                set: { _ in }
            )) {
                AuthenticationView()
            }
        }
    }
}

3. 单例方案的优化建议

你当前用单例监听Firebase Auth状态的方案本身没有问题,不过更符合SwiftUI规范的做法是在App入口实例化后通过环境对象注入,这样可以避免全局单例的硬编码依赖,后续做单元测试也更容易mock:

  1. 先在你的App主入口文件里注入:
@main
struct YourApp: App {
    // 初始化Authenticator,不需要再用全局shared了
    @StateObject private var authenticator = Authenticator()
    
    var body: some Scene {
        WindowGroup {
            AccountView()
                .environmentObject(authenticator)
        }
    }
}
  1. 把Authenticator里的static let shared = Authenticator()可以删掉,之后所有子视图需要用的时候,直接声明@EnvironmentObject var authenticator: Authenticator就可以拿到同一个实例,不用每次绑定单例。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.08 10:48:04