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

SwiftUI:如何通过Published属性实现Sheet的显示控制

解决ObservableObject中@Published变量与Sheet的Binding参数适配问题

问题核心

你在遵循ObservableObject的ViewModel中使用@Published private(set) var isVisable = false封装状态,希望通过它控制Sheet的显示,但sheet(isPresented:)参数要求Binding<Bool>类型:

  • 直接使用model.$isVisable会得到Published<Bool>.Publisher而非Binding,触发编译错误;
  • 若尝试使用$model.isVisable,又会因isVisable的private(set)修饰符导致外部无法修改状态,同样报错;
  • 你希望避免在View中添加@State变量并通过回调同步,保持架构清晰。

解决方案1:在ViewModel中提供自定义Binding(推荐)

在ViewModel内部添加一个计算属性,封装对应isVisable的Binding<Bool>,既保持状态的封装性,又能满足View的Binding需求:

修改后的ViewModel代码:

final class ContentViewModel: ObservableObject {
    @Published private(set) var isVisable = false
    
    func toggleVisability() {
        isVisable.toggle()
    }
    
    // 供View调用的Binding属性
    var isVisableBinding: Binding<Bool> {
        Binding(
            get: { self.isVisable },
            set: { newValue in
                guard newValue != self.isVisable else { return }
                self.isVisable = newValue
                // 可在此添加状态变更后的额外逻辑(如日志、事件上报等)
            }
        )
    }
}

对应的ContentView代码:

struct ContentView: View {
    @StateObject var model = ContentViewModel()

    var body: some View {
        VStack {
            Button("Show") {
                model.toggleVisability()                
            }
            // 使用ViewModel提供的Binding控制Sheet
            .sheet(isPresented: model.isVisableBinding) {
                Text("Placeholder")
            }
        }
    }
}

关键说明:

  • 当用户手动关闭Sheet时,isPresented会自动将Binding的value设为false,此时Binding的set块会同步更新ViewModel中的isVisable,无需额外添加onDismiss回调(原代码中onDismiss调用toggleVisability会导致状态反转错误);
  • 该方案完全遵循MVVM架构,View仅负责展示和触发ViewModel的方法,状态逻辑全部封装在ViewModel内,无架构污染。

解决方案2:移除封装(不推荐)

若暂时忽略封装性,可移除isVisable的private(set)修饰符,直接使用@Published var isVisable = false,然后在View中用$model.isVisable作为Binding参数:

// 修改后的ViewModel
final class ContentViewModel: ObservableObject {
    @Published var isVisable = false // 移除private(set)
    func toggleVisability() {
        isVisable.toggle()
    }
}

// View中使用
.sheet(isPresented: $model.isVisable) {
    Text("Placeholder")
}

但此方式破坏了ViewModel的封装性,外部可直接修改状态,不符合架构设计原则,仅作为临时变通方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 20:05:36