绑定@ObjectBinding 和@EnvironmentObject [英] binding with @ObjectBinding and @EnvironmentObject

查看:55
本文介绍了绑定@ObjectBinding 和@EnvironmentObject的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

28-07-2019.我仍然对下面的代码有疑问.我想将数据模型与 ContentView 分开.所以我制作了一个单独的文件并添加了类,如下所示:

28-07-2019. I still have a question about the code below. I would like to separate the data model out of the ContentView. So I made a separate file and added the class, like this:

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

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

This doesn't work however, I'm getting an error:

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

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

Error occurs on the .environmentObject(User()) line in # if DEBUG.

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

After watching some video's I made the following code (including changes for Xcode 11 beta 4). Tips from both answers from dfd and MScottWaller are already included in the code.

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() 视图中.答案是:

But now onto the next part. If I have another view... how can I reference the data then? Since the source of truth is in the above ViewContent() view. The answer is:

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 的回答):

Don't forget to edit the SceneDelegate (answer from 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()
    }
} 

推荐答案

如果事实来源"是 User,并且您已将其设为 BindableObject只需要最好地公开它以使其可用于您想要的各种视图.我建议 @EnvironmentObject.

If the "source of truth" is User, and you've made it a BindableObject you just need to expose it best to make it available to the various views you want. I suggest @EnvironmentObject.

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

In your SceneDelegate, do this:

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

既然用户的有状态"实例可用于任何视图,您只需添加:

Now that a "stateful" instance of User is available to any View, you simply need to add:

@EnvironmentObject var user: User

致任何/所有需要了解 User 的用户.

To any/all vies that need to know about User.

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

BindableObject (for the most part) reserve memory for what you've denied. @ObjectBinding merely binds a view to what is in that part of memory (again, for the most part). And yes, you can do this for User in all views - but since you are instantiating it in ContentView? Nope.)! @EnvironmentObject makes it available to any views that need to access it.

当然,您可以使用 @ObjectBinding 代替 @EnvironmentObject,但到目前为止,?我从来没有听说过这样做的理由.

Absolutely, you can use @ObjectBinding instead of an @EnvironmentObject, but so far,? I've never heard of a reason to do that.

这篇关于绑定@ObjectBinding 和@EnvironmentObject的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆