I've been frustrated for months trying to get previews to work in Xcode when Core Data is used. I can definitely make the preview work for the views that don't include an injected item. But not any subsequent views that depend on the source Entity.
Let's say I have a master/detail-like SwiftUI project with ContentView containing my list and ThingDetailView showing the detail for the entity Thing.
I created a wrapper for the preview of ContentView:
struct PreviewCoreDataWrapper<Content: View>: View {
@Environment(\.managedObjectContext) private var viewContext
let content: (NSManagedObjectContext) -> Content
var body: some View {
let managedObjectContext = viewContext
let sampleThing = Thing(context: managedObjectContext)
sampleThing.name = "Sample Name"
sampleThing.comment = "Sample Comment"
sampleThing.id = UUID()
//more attributes
return self.content(managedObjectContext)
}
init(@ViewBuilder content: @escaping (NSManagedObjectContext) -> Content) {
self.content = content
}
}
Then the preview in ContentView, this WORKS.
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
PreviewCoreDataWrapper { managedObjectContext in
ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
}
But then in the detail view, no matter what I have tried, I cannot get the preview to work. I've tried dozens of ways to satisfy the injected Thing call.
struct ThingDetailView_Previews: PreviewProvider {
static var previews: some View {
PreviewCoreDataWrapper { managedObjectContext in
ThingDetailView(thing: sampleThing).environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
}
Any guidance would be greatly appreciated. Xcode Version 12.0 (12A7208), iOS 14
The ThingDetailView is pretty standard stuff:
struct ThingDetailView: View {
@Environment(\.presentationMode) var presentationMode
@Environment(\.managedObjectContext) private var managedObjectContext
var thing: Thing
@State private var localName: String = ""
@State private var localComment: String = ""
//bunch more properties
var body: some View {
DispatchQueue.main.async {
self.localName = self.thing.wrappedName
self.localComment = self.thing.wrappedComment
//buch more properties
}//dispatch
DispatchQueue.main.async {
if !showEditView {
localNewUIImage = UIImage(data: thing.tImage)
}
}
//you need this to allow the TextEditor background to be changed
UITextView.appearance().backgroundColor = .clear
return ScrollView(.vertical, showsIndicators: false) {
VStack(alignment: .center, spacing: 20) {
//this is for an image
if !showEditView {
ThingHeaderView(thing: thing)
} else {
NewPhotoView(myImage: $localMyImage, newUIImage: $localNewUIImage, disableSaveButton: $localDisableSaveButton)
}
VStack(alignment: .leading, spacing: 10) {
Group {//group 1
//MARK: name
VStack (alignment: .leading) {
if !showEditView {
Text("\(thing.wrappedName)")
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.vertical, 5)
.textFieldStyle(RoundedBorderTextFieldStyle())
.font(.system(size: 22, weight: .bold, design: .default))
.foregroundColor(Color("CardBlue"))
} else {
TextField("tf name", text: self.$localName)
.modifier(TextFieldSetup())
}
}//name v
//MARK: comment
VStack (alignment: .leading) {
if !showEditView {
Text("\(thing.wrappedComment)")
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.vertical, 5)
.textFieldStyle(RoundedBorderTextFieldStyle())
.font(.headline)
} else {
TextEditor(text: self.$localComment)
.modifier(TextEditorSetup())
}
}//comment v
//bunch more TextFields and TextEditors
}//group 1
}//inner v
.padding(.horizontal, 20)
.frame(maxWidth: 640, alignment: .center)
}//outer v
//seems like a title is needed to remofe large space at top - then hide
.navigationBarTitle(thing.wrappedName, displayMode: .inline)
//.navigationBarHidden(true)
.navigationBarBackButtonHidden(showEditView)
.navigationBarItems(
leading:
Button(action: {
if showEditView {
self.showEditView = false
}
}) {
Text(showEditView ? "Cancel" : "")
.font(.system(size: 20))
},
trailing:
Button(action: {
if !showEditView {
print("showEditView is \(showEditView)")
self.showEditView = true
} else {
self.saveEditedRecord()
self.showEditView = false
}
}) {
Image(systemName: self.showEditView ? "square.and.arrow.down.fill" : "square.and.pencil")
.font(.system(size: 25))
.frame(width: 60, height: 60)
}//images
.disabled(self.localDisableSaveButton)
)//nav bar item
}//scroll
.navigationViewStyle(StackNavigationViewStyle())
}//body
func saveEditedRecord() {
print("...and you are in the save function...")
let context = self.managedObjectContext
thing.id = UUID()
//all the rest and
//standard Core Data save
}//save record
}//struct thing detail view
And the ThingHeaderView:
struct ThingHeaderView: View {
@Environment(\.horizontalSizeClass) var sizeClass
@State private var isAnimatingImage: Bool = false
var thing: Thing
var body: some View {
let sc = sizeClass == .compact
return ZStack {
if UIImage(data: thing.tImage) != nil {
Image(uiImage: UIImage(data: thing.tImage)!)
.resizable()
.renderingMode(.original)
.aspectRatio(contentMode: .fit)
.cornerRadius(10)
.shadow(color: (Color.black.opacity(0.5)), radius: 8, x: 10, y: 10)
.padding(sc ? 6 : 10)
} else {
Image(systemName: "camera.circle.fill")
.resizable()
.frame(width: 60, height: 60, alignment: .center)
}
}
.onAppear {
withAnimation(.easeOut(duration: 0.5)) {
isAnimatingImage = true
}
}
}
}
from SwiftUI Preview Does Not Work with Core Data when Entity Injected in View
No comments:
Post a Comment