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:
- 先在你的App主入口文件里注入:
@main struct YourApp: App { // 初始化Authenticator,不需要再用全局shared了 @StateObject private var authenticator = Authenticator() var body: some Scene { WindowGroup { AccountView() .environmentObject(authenticator) } } }
- 把Authenticator里的
static let shared = Authenticator()可以删掉,之后所有子视图需要用的时候,直接声明@EnvironmentObject var authenticator: Authenticator就可以拿到同一个实例,不用每次绑定单例。
内容的提问来源于stack exchange,提问作者TommyBs
相关产品推荐
相关产品推荐

