无法将“Published<Bool>.Publisher”类型的值转换为预期的参数类型“Binding<Bool>”

Posted

技术标签:

【中文标题】无法将“Published<Bool>.Publisher”类型的值转换为预期的参数类型“Binding<Bool>”【英文标题】:Cannot convert value of type 'Published<Bool>.Publisher' to expected argument type 'Binding<Bool>' 【发布时间】:2020-11-26 15:28:51 【问题描述】:

尝试编译以下代码时:

class LoginViewModel: ObservableObject, Identifiable 
    @Published var mailAdress: String = ""
    @Published var password: String = ""
    @Published var showRegister = false
    @Published var showPasswordReset = false

    private let applicationStore: ApplicationStore

    init(applicationStore: ApplicationStore) 
        self.applicationStore = applicationStore
    

    var passwordResetView: some View 
        PasswordResetView(isPresented: $showPasswordReset) // This is where the error happens
    

PasswordResetView 如下所示:

struct PasswordResetView: View 
    @Binding var isPresented: Bool
    @State var mailAddress: String = ""
    
    var body: some View 
            EmptyView()
        
    

我得到错误编译错误

Cannot convert value of type 'Published<Bool>.Publisher' to expected argument type 'Binding<Bool>'

如果我在 LoginViewModel 类之外使用发布的变量,它就可以正常工作:

struct LoginView: View 
    @ObservedObject var viewModel: LoginViewModel

    init(viewModel: LoginViewModel) 
      self.viewModel = viewModel
    
    
    var body: some View 
            PasswordResetView(isPresented: self.$viewModel.showPasswordReset)
    

有什么建议我在这里做错了吗?我有机会从所属类内部将已发布的变量作为绑定传递吗?

谢谢!

【问题讨论】:

投影值(以 $ 开头的东西)在不同的上下文中可能不同。如果第一种情况@Published 生成发布者预计值,则第二种情况下的绑定预计值由@ObservedObject 生成。问题是你想做什么,为什么要放置 View Insider 视图模型? 我遵循 Ray Wenderlich Sitze (raywenderlich.com/4161005-mvvm-with-combine-tutorial-for-ios) 上描述的 MVVM 原则。他们将所有路由逻辑放在视图的视图模型中(包括视图的实例化和配置+它们的模型)。这基本上就是我在这里想要做的。 最好遵循 SwiftUI 原则,MVVM 并不真正适合已经解决所有问题的 SwiftUI。 【参考方案1】:

** 对于 Combine 和 SwiftUI 来说还是新手,所以不确定是否有更好的方法来处理 **

您可以从发布者初始化绑定。

https://developer.apple.com/documentation/swiftui/binding/init(get:set:)-6g3d5

let binding = Binding(
    get:  [weak self] in
        (self?.showPasswordReset ?? false)
    ,
    set:  [weak self] in
        self?.showPasswordReset = $0
    
)

PasswordResetView(isPresented: binding)

【讨论】:

谢谢。我也看到了,但它看起来有点笨拙。但我现在就试试看:-) 我在范围内找不到“自我”【参考方案2】:

这是一种可能的方法 - 在生成的视图中进行可能的观察并避免工厂和演示者之间的紧密耦合的想法。

使用 Xcode 12 / iOS 14 测试(对于旧系统可能需要进行一些调整)

protocol ResetViewModel 
    var showPasswordReset: Bool  get set 


struct PasswordResetView<Model: ResetViewModel & ObservableObject>: View 
    @ObservedObject var resetModel: Model

    var body: some View 
        if resetModel.showPasswordReset 
            Text("Show password reset")
         else 
            Text("Show something else")
        
    


class LoginViewModel: ObservableObject, Identifiable, ResetViewModel 
    @Published var mailAdress: String = ""
    @Published var password: String = ""
    @Published var showRegister = false
    @Published var showPasswordReset = false

    private let applicationStore: ApplicationStore

    init(applicationStore: ApplicationStore) 
        self.applicationStore = applicationStore
    

    var passwordResetView: some View 
        PasswordResetView(resetModel: self)
    

【讨论】:

这是一个非常聪明的想法,只需将 ObservedObject 放在视图中并防止任何类型问题。我用它来更新一个进度条,该进度条正在另一个线程中监视已发布的进度值并且运行良好。【参考方案3】:

我认为这里要理解的重要一点是“$”在组合上下文中的作用。

“$”所做的是将变量“showPasswordReset”的变化发布在被观察的地方。

当它在一个类型之前时,它不代表你为变量声明的类型(在这种情况下是布尔值),它代表一个发布者,如果你想要类型的值,只需删除“$”即可。

"$" 用于将变量标记为@ObservedObject 的上下文中, (这里的 ObservableObject 是 LoginViewModel,您订阅它以作为发布者监听其变量市场的变化)

struct ContentView: View 
       @ObservedObject var loginViewModel: LoginViewModel...

在这种情况下(例如 ContentView),“showPasswordReset”的更改将在其值更新时“发布”,因此视图会使用新值更新。

【讨论】:

【参考方案4】:

对于指出“无法将 'Binding' 类型的值转换为预期的参数类型 'Bool'”的错误,解决方案是使用 WrappedValue,如下例所示。

如果您有带有属性 isEnabled 的 MyObject 对象,并且您需要将其用作 vanilla Bool 类型而不是“绑定”,那么执行此操作 myView.disabled($myObject.isEnabled.wrappedValue)

【讨论】:

以上是关于无法将“Published<Bool>.Publisher”类型的值转换为预期的参数类型“Binding<Bool>”的主要内容,如果未能解决你的问题,请参考以下文章

为啥我无法正确获取图像数据或无法将数据发送到服务器?

无法将 createdAt 和 updatedAt 保存为日期时间值,也无法将后端保存为前端

C# 无法将类型为“System.Byte[]”的对象强制转换为类型“System.Data.DataTable

无法将类型为“System.Collections.Generic.List`1[EPMS.Domain.SingleItem]”的对象强制转换为类型“EPMS

无法将 .json 文件从 CSV 下载到 JSON 转换并且无法将 JSON 转换为 CSV

无法将 ReactiveUI 添加到 NUnit 测试项目