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

时间:2020-08-06 11:14:32

标签: ios swift swiftui combine

尝试编译以下代码时:

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)
    }
}

有人建议我在这里做错了吗?我有机会可以从拥有的类内部将已发布的变量作为绑定传递吗?

谢谢!

4 个答案:

答案 0 :(得分: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)

答案 1 :(得分:1)

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

在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)
    }
}

答案 2 :(得分:0)

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

如果您具有具有isEnabled属性的MyObject对象,并且需要将其用作普通Bool类型而不是“ Binding”,请执行此操作 myView.disabled($ myObject.isEnabled.wrappedValue)

答案 3 :(得分:0)

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

“$”的作用是将变量“showPasswordReset”的变化发布到它被观察到的地方。

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

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

const { loading as userLoading, error as userError, data as userData} = user;
const { loading as customerLoading, error as customerError, data as customerData} = customer;

在该上下文中(例如 ContentView),“showPasswordReset”的更改将在其值更新时“发布”,以便使用新值更新视图。