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

Jan*_*och 17 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
    }
}
Run Code Online (Sandbox Code Playgroud)

其中 PasswordResetView 看起来像这样:

struct PasswordResetView: View {
    @Binding var isPresented: Bool
    @State var mailAddress: String = ""
    
    var body: some View {
            EmptyView()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我收到错误编译错误

Cannot convert value of type 'Published<Bool>.Publisher' to expected argument type 'Binding<Bool>'
Run Code Online (Sandbox Code Playgroud)

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

struct LoginView: View {
    @ObservedObject var viewModel: LoginViewModel

    init(viewModel: LoginViewModel) {
      self.viewModel = viewModel
    }
    
    var body: some View {
            PasswordResetView(isPresented: self.$viewModel.showPasswordReset)
    }
}
Run Code Online (Sandbox Code Playgroud)

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

谢谢!

Big*_*air 133

不知道为什么这里提出的解决方案如此复杂,而对此有一个非常直接的解决方案。

在类似的 Reddit 问题上找到了这个答案:

问题是,当您应该访问 @ObservedObject(这是一个 Binding)的投影值时,您正在访问 @Published(这是一个 Publisher)的投影值,也就是说:您拥有您globalSettings.$tutorialView应该拥有的位置$globalSettings.tutorialView

  • 这应该是公认的答案! (8认同)
  • 这很有道理,我不知道为什么我没有考虑过。哈哈 (3认同)
  • 大多数人可能有“globalSettings.$tutorialView”,因为这就是 XCode 在您打字时建议的内容。 (2认同)

and*_*wan 10

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

“$”的作用是发布正在观察的变量“showPasswordReset”的更改。

当它位于类型之前时,它不代表您为变量声明的类型(在本例中为布尔值),它代表发布者,如果您想要该类型的值,只需删除“$”即可。

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

struct ContentView: View {
       @ObservedObject var loginViewModel: LoginViewModel...
Run Code Online (Sandbox Code Playgroud)

在该上下文中(例如 ContentView),“showPasswordReset”的更改将在其值更新时“已发布”,因此视图会使用新值进行更新。


kth*_*rat 5

** 对结合和 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)

Run Code Online (Sandbox Code Playgroud)

  • 谢谢。我也看到了,但看起来有点笨拙。但我现在就尝试一下:-) (2认同)
  • 我得到 Cannot find 'self' in range (2认同)

Asp*_*eri 4

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

使用 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)
    }
}
Run Code Online (Sandbox Code Playgroud)