首页 > 解决方案 > 与@ObjectBinding 和@EnvironmentObject 绑定

问题描述

2019 年 7 月 28 日。我仍然对下面的代码有疑问。我想将数据模型从 ContentView 中分离出来。所以我制作了一个单独的文件并添加了类,如下所示:

import SwiftUI
import Combine

class User: BindableObject {
    let willChange = PassthroughSubject<Void, Never>()
    var username : String = "Jan" { willSet { willChange.send() }}
    var password : String = "123456" { willSet { willChange.send() } }
    var emailAddress : String = "jan@mail.nl" { willSet { willChange.send() } }
}

#if DEBUG
struct User_Previews: PreviewProvider {
    static var previews: some View {
        User()
            .environmentObject(User())
    }
}
#endif

但是这不起作用,我收到一个错误:

Protocol type 'Any' cannot conform to 'View' because only concrete types can conform to protocols

# if DEBUG 中的 .environmentObject(User()) 行发生错误。


在观看了一些视频后,我编写了以下代码(包括 Xcode 11 beta 4 的更改)。来自 dfd 和 MScottWaller 的两个答案的提示已包含在代码中。

import Combine
import SwiftUI

class User: BindableObject {
    let willChange = PassthroughSubject<Void, Never>()
    var username = "Jan" { willSet { willChange.send() } }
    var password = "123456" { willSet { willChange.send() } }
    var emailAddress = "jan@mail.nl" { willSet { willChange.send() } }
}

struct ContentView: View {
    @EnvironmentObject var user: User

    private func buttonPressed() {
        print(user.username) // in Simulator
    }

    var body: some View {
        VStack {
            TextField("Username", text: $user.username)
            TextField("Password", text: $user.password)
            TextField("Emailaddress", text: $user.emailAddress)
            Button(action: buttonPressed) {
                Text("Press me!")
            }

        }
    }
}

#if DEBUG
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
            .environmentObject(User())
    }
}
#endif

但现在进入下一部分。如果我有另一种看法……那我该如何参考数据呢?由于事实的来源在上面的 ViewContent() 视图中。答案是:

import SwiftUI

struct DetailView: View {
    @EnvironmentObject var user: User

    var body: some View {
        VStack {
            TextField("Username", text: $user.username)
            TextField("Password", text: $user.password)
            TextField("Email", text: $user.emailAddress)
        }
    }
}

#if DEBUG
struct DetailView_Previews: PreviewProvider {
    static var previews: some View {
        DetailView()
            .environmentObject(User())
    }
}
#endif

不要忘记编辑 SceneDelegate(来自 dfd 的答案):

var user = User()

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: ContentView()
            .environmentObject(user)
        )
        self.window = window
        window.makeKeyAndVisible()
    }
} 

标签: swiftui

解决方案


在您的 DetailView 预览中,不要为 get 附加 environmentObject。看看我是如何在下面的 PreviewProvider 中添加它的。当您运行实际的应用程序时,您需要对 SceneDelegate 中的 ContentView 执行相同的操作

import SwiftUI

struct DetailView: View {
    @EnvironmentObject var user: User

    var body: some View {
        HStack {
            TextField("Username", text: $user.username)
            Text("Hello world!")
        }
    }
}

#if DEBUG
struct DetailView_Previews: PreviewProvider {
    static var previews: some View {
        DetailView()
            .environmentObject(User())
    }
}
#endif

推荐阅读