可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
The Xcode preview does not work if i add a EnviromentObject
property wrapper. Everytime i add one the Canvas doesn't build and i get this error:
Cannot preview in this file - [App Name].app may have crashed
If i replace the EnviromentObject
property wrapper with ObservedObject
and initialize it everything works fine.
Here's my code:
class NetworkManager: ObservableObject {
}
struct ContentView : View {
@EnvironmentObject var networkManager: NetworkManager
var body: some View {
Text("Canvas not working")
}
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environmentObject(NetworkManager())
}
}
#endif
Update:
It doesn’t load the preview as well when i am using a binding:
struct ContentView : View {
@EnvironmentObject var networkManager: NetworkManager
@Binding var test123: String
var body: some View {
Text("Canvas not working")
}
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
@State static var test1 = ""
static var previews: some View {
ContentView(test123: $test1).environmentObject(NetworkManager())
}
}
#endif
回答1:
I'm assuming based on the code you provided that your SceneDelegate
looks like this:
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: ContentView())
self.window = window
window.makeKeyAndVisible()
}
I'm not going to pretend I know exactly what the canvas is doing behind the scenes when it generates a preview, but based on the fact that the error specifically states that the app may have crashed, I'm assuming that it's attempting to launch the entire app when it tries to generate a preview. Maybe it needs to use the SceneDelegate
to launch the preview, maybe it's something else entirely - I can't say for sure.
Regardless, the reason the app is crashing is because you aren't passing an environment object in your SceneDelegate
. Your SceneDelegate
should look like this:
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: ContentView().environmentObject(NetworkManager()))
self.window = window
window.makeKeyAndVisible()
}
回答2:
As @graycampbell suggested, you need to ensure that the EnvironmentObject is provided to your ContentView in the SceneDelegate. While a lot of the preview / canvas mechanics are in a black box, Xcode's UI would suggest that invoking a new preview or refreshing an existing one, builds (or updates relevant parts of) a variant of your app even for the regular preview, as opposed to the "Live Preview". This process can fail if the SceneDelegate isn't set up correctly.
For your @Binding problem, Binding.constant(_:) should help. Per the SwiftUI Documentation .constant does the following:
Creates a binding with an immutable value.
This is what you want for your preview, instead of the @State your sample code shows. You can see an example of .constant in use in Section 3 of this Apple tutorial.
So instead of this:
#if DEBUG
struct ContentView_Previews: PreviewProvider {
@State static var test1 = "Some Preview String"
static var previews: some View {
ContentView(test123: $test1)
.environmentObject(NetworkManager())
}
}
#endif
you can do the following:
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(test123: .constant("Some Preview String"))
.environmentObject(NetworkManager())
}
}
#endif
With this change, previews of your code work perfectly for me.
Keep in mind that you again need to provide a value to this Binding in your SceneDelegate or any other place in which you use this particular ContentView. Otherwise you will run into a problem similar to the one you faced with EnvironmentObject, just that this particular omission luckily is highlighted by a compiler error.
回答3:
Looks like Xcode issue. Try to use blue button instead of "Try Again".
回答4:
I do have a workaround for this, but yes that's true that bug is in XCode.
To fix this. you must set up your SceneDelegate
first.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: ContentView().environmentObject(NetworkManager()))
self.window = window
window.makeKeyAndVisible()
}.
You must set up your preview too. As seems you have already done that.
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environmentObject(NetworkManager())
}
}.
At last, just declare one @State
variable inside your ContentView
, of the same type you @Published
your @EnvironmentObject
, whether or not you use it for binding anywhere in ContentView
doesn't matter.
@State var bindingVar: Double = 0.0
As I said earlier that it's XCode bug and I don't know why ContentView
needs one @State variable of the same type to start previewing @EnvironmentObject
bound code.
May be ContentView_Previews
unable to find out binding from @EnvironmentObject
.
You can see below in my code, it works like a charm.
回答5:
I had the same problem, and I found out what the reason was.
I simply forgot to add the .environmentObject()
modifier
to ContentView()
in the preview part.
struct Content_Previews: PreviewProvider {
static var previews: some View {
ContentView().environmentObject(NetworkManager())
}
}
That was why Xcode built it, without showing a code-mistake,
but crashed on preview in canvas. - Simple mistake, I know.