Hello,
I’m building a Edit View, that allows a user to make updates to a list item and save it back to core data and the list. To do this I am passing in the values from the list in the appropriate fields using the .onAppear action.
All data is passed in appropriately, and the toggle and text fields allow me to make changes easily and save them back to core data and update the list item. However, if I try to change the picker value (emojiChoice) and select a new one, the picker value does not change or save back to Core Data.
When I comment out the onAppear action for the picker variable (emojiChoice), the picker now allows me to choose the value I want and save it back to Core Data. I also have another view that allows a user to create items that is near identical to this Edit View, minus the onAppear action, that works as well. The picker allows a user to choose and emoji. Because there are alot of emoji’s, I created an array and instead am just passing the array index of a selected emoji as an Int.
How can I fix this to allow the picker to show the value to be edited and also allow it to be changed?
Here’s the code of the Edit View:
import CoreData
import SwiftUI
struct editRemindr: View {
@Environment(\.managedObjectContext) var moc
@Environment(\.presentationMode) var presentationMode
let emojiList = EmojiList()
@ObservedObject var reminder: ReminderEntity
@State private var showingDeleteAlert = false
// Form Variables
@State var notifyOn = true
@State var emojiChoice = 0
@State var notification: String
@State var notes: String
// Delete Reminder Function
func deleteAction() {
moc.delete(reminder)
// try? self.moc.save()
presentationMode.wrappedValue.dismiss()
}
// View Controller
var body: some View {
Form {
// On/Off Toggle
Toggle(isOn: $notifyOn) {
Text("On/Off")
}
// Emoji Picker
Picker(selection: $emojiChoice, label: Text("Emoji")) {
ForEach(0 ..< emojiList.emojis.count) {
Text(self.emojiList.emojis[$0])
}
}
// Notification Text
Section(header: Text("NOTIFICATION")) {
HStack {
TextField("Write your notification...", text: $notification)
.onReceive(notification.publisher.collect()) {
self.notification = String($0.prefix(60)) // <---- SET CHARACTER LIMIT
}
Text("\(notification.count) / 60")
.font(.caption)
.foregroundColor(.gray)
}
}
// Notes Text
Section(header: Text("NOTES")) {
VStack {
MultiLineTextField(text: $notes).frame(numLines: 6)
.padding(.top, 5)
.onReceive(notes.publisher.collect()) {
self.notes = String($0.prefix(240)) // <---- SET CHARACTER LIMIT
}
Text("\(notes.count) / 240")
.font(.caption)
.foregroundColor(.gray)
.frame(maxWidth: .infinity, alignment: .trailing)
}
}
// Save Changes Button
Section() {
Button (action: {
self.reminder.dateCreated = Date()
self.reminder.notifyOn = self.notifyOn
self.reminder.emojiChoice = Int64(self.emojiChoice)
self.reminder.notification = self.notification
self.reminder.notes = self.notes
try? self.moc.save()
self.presentationMode.wrappedValue.dismiss()
}) {
Text("SAVE CHANGES")
.fontWeight(.bold)
.foregroundColor(.white)
.font(.body)
} .padding()
.frame(maxWidth: .infinity)
.background(Color.green)
.padding(.vertical, -6)
.padding(.horizontal, -15)
// Cancel Button
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
Text("Cancel")
.frame(maxWidth: .infinity)
}
}
}
// Make List Items Appear in Fields
.onAppear(perform: {
self.notifyOn = self.reminder.notifyOn
self.emojiChoice = Int(self.reminder.emojiChoice)
self.notification = self.reminder.notification ?? "unknown"
self.notes = self.reminder.notes ?? "unknown"
})
.alert(isPresented: $showingDeleteAlert) {
Alert(title: Text("Delete Reminder"), message: Text("Are you sure you want to delete this Reminder?"), primaryButton: .destructive(Text("Delete")) {
self.deleteAction()
}, secondaryButton: .cancel()
)
}
.navigationBarTitle("Edit Reminder")
.navigationBarItems(trailing: Button(action: {self.showingDeleteAlert = true
}) {
Image(systemName: "trash")
})
}
// Creates Text Limits
class TextLimit: ObservableObject {
@Published var text = "" {
didSet {
if text.count > characterLimit && oldValue.count <= characterLimit {
text = oldValue
}
}
}
let characterLimit: Int
init(limit: Int = 5){
characterLimit = limit
}
}
Here’s the code for the Content View with the list if it helps as well.
import SwiftUI
import CoreData
struct ContentView: View {
@Environment(\.managedObjectContext) var moc
@FetchRequest(entity: ReminderEntity.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \ReminderEntity.dateCreated, ascending: false)])
var reminder: FetchedResults<ReminderEntity>
@State private var showingAddScreen = false
@State var showWelcomeScreen = false
let emojiList = EmojiList()
//Toggle Control
@State var notifyOn = true
// Save Items Function
func saveItems() {
do {
try moc.save()
} catch {
print(error)
}
}
// Delete Item Function
func deleteItem(indexSet: IndexSet) {
let source = indexSet.first!
let listItem = reminder[source]
moc.delete(listItem)
}
// View Controller
var body: some View {
VStack {
NavigationView {
ZStack (alignment: .top) {
// List View
List {
ForEach(reminder, id: \.self) { notification in
NavigationLink(destination: editRemindr(reminder: notification, notification: notification.notification ?? "unknown", notes: notification.notes ?? "unknown")) {
// Text within List View
HStack {
// MARK: TODO
// Toggle("NotifyOn", isOn: true)
// .labelsHidden() // Hides the label/title
Text("\(self.emojiList.emojis[Int(notification.emojiChoice)]) \(notification.notification!)")
}
}
}
.onDelete(perform: deleteItem)
}
// Navigation Items
.navigationBarTitle("", displayMode: .inline)
.navigationBarItems(
leading:
HStack {
Button(action: {
self.showWelcomeScreen.toggle()
}) {
Image(systemName: "info.circle.fill")
.font(.system(size: 24, weight: .regular))
}.foregroundColor(.gray)
// Positioning Remindr Logo on Navigation
Image("remindrLogoSmall")
.resizable()
.aspectRatio(contentMode: .fit)
//.frame(width: 60, height: 60, alignment: .center)
.padding(.leading, 83)
.padding(.top, -10)
},
// Global Settings Navigation Item
trailing: NavigationLink(destination: globalSettings()){
Image("settings")
.font(Font.title.weight(.ultraLight))
}.foregroundColor(.gray)
)
// Add New Reminder Button
VStack {
Spacer()
Button(action: { self.showingAddScreen.toggle()
}) {
Image("addButton")
.renderingMode(.original)
}
.sheet(isPresented: $showingAddScreen) {
newRemindr().environment(\.managedObjectContext, self.moc)
}
}
}
} .sheet(isPresented: $showWelcomeScreen) {
welcomeScreen()
}
}
}
Any help would be greatly appreciated! Thank you so much!