与@ObjectBinding 和@EnvironmentObject 绑定
binding with @ObjectBinding and @EnvironmentObject
28-07-2019。我仍然对下面的代码有疑问。我想将数据模型从 ContentView 中分离出来。所以我制作了一个单独的文件并添加了 class,如下所示:
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()
}
}
如果 "source of truth" 是 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 的 "stateful" 实例可用于 any 视图,您只需添加:
@EnvironmentObject var user: User
需要了解 User
的 any/all 观点。
BindableObject
(大多数情况下)为您拒绝的内容保留内存。 @ObjectBinding
只是将一个视图绑定到那部分内存中的内容(同样,大部分情况下)。是的,您 可以 在所有视图中为 User
执行此操作 - 但既然您是在 ContentView
中实例化它?没有。)! @EnvironmentObject
使其可供需要访问它的任何视图使用。
当然,您可以使用 @ObjectBinding
代替 的 @EnvironmentObject
,但是到目前为止,?我从来没有听说过这样做的理由。
在您的 DetailView 预览中,不要附加 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
28-07-2019。我仍然对下面的代码有疑问。我想将数据模型从 ContentView 中分离出来。所以我制作了一个单独的文件并添加了 class,如下所示:
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()
}
}
如果 "source of truth" 是 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 的 "stateful" 实例可用于 any 视图,您只需添加:
@EnvironmentObject var user: User
需要了解 User
的 any/all 观点。
BindableObject
(大多数情况下)为您拒绝的内容保留内存。 @ObjectBinding
只是将一个视图绑定到那部分内存中的内容(同样,大部分情况下)。是的,您 可以 在所有视图中为 User
执行此操作 - 但既然您是在 ContentView
中实例化它?没有。)! @EnvironmentObject
使其可供需要访问它的任何视图使用。
当然,您可以使用 @ObjectBinding
代替 的 @EnvironmentObject
,但是到目前为止,?我从来没有听说过这样做的理由。
在您的 DetailView 预览中,不要附加 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