与@ObjectBinding和@EnvironmentObject绑定

时间:2019-07-26 21:43:39

标签: swiftui

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

/.*-ad[0-9].png/

这不起作用,但出现错误:

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

如果已调试,则#中的.environmentObject(User())行上会发生错误。


观看完一些视频后,我做了以下代码(包括对Xcode 11 beta 4的更改)。 dfd和MScottWaller的答案都已包含在代码中。

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

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

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

别忘了编辑SceneDelegate(来自dfd的答案):

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

2 个答案:

答案 0 :(得分:3)

在DetailView预览中,请勿附加环境对象。请参阅下面的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

答案 1 :(得分:1)

如果“真理的源头”是User,而您将其设为BindableObject,则只需公开它以使其可用于所需的各种视图即可。我建议@EnvironmentObject

在您的SceneDelegate中,执行以下操作:

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

现在任何视图都可以使用User的“有状态”实例,您只需要添加:

@EnvironmentObject var user: User

针对需要了解User的任何/所有对手。

BindableObject(大部分情况下)为您拒绝的内容保留内存。 @ObjectBinding仅将视图绑定到内存的那部分(大部分也是)。是的,您可以 在所有视图中为User执行此操作-但由于您是在ContentView中实例化它的?不。)! @EnvironmentObject使其可用于需要访问它的所有视图。

当然可以使用@ObjectBinding的{​​{1}}代替 ,但是到目前为止?我从未听说过这样做的理由。