Douglas Nascimento
Douglas Nascimento

Reputation: 11

How to save edited content in SwiftUI?

My goal is to save the contents only if the user taps "Save", but to discard the edits when they click "Cancel". That's why I created two variables that receive the initial values of note.content and note.title. Those are the ones that users will actually edit. My aim is to take the edited values and assign them to the note instance, which is what I want to edit, only when the user taps "Save". However, this is not happening because when I tap "Save", note.content and note.title remain the same value as before and don't reflect the changes. I don't know what else to do; if someone could help me with this, I would appreciate it.

struct EditNoteView: View {
    @State var note: Note
    @Environment(\.dismiss) var dismiss
    @State var editedTitle: String
    @State var editedContent: String
    
    var body: some View {
        NavigationStack {
            VStack {
                TextEditor(text: $editedTitle)
                    .font(.title)
                    .fontWeight(.bold)
                    .frame(width: UIScreen.main.bounds.width, height: 50, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
                Divider()
                
                TextEditor(text: $editedContent)
                    .font(.body)
                    .frame(maxHeight:.infinity)
            }
        }
        .navigationBarBackButtonHidden(true)
        .toolbar {
            ToolbarItem(placement: .topBarLeading) {
                Button("Cancel") {
                    dismiss()
                }
            }
            ToolbarItem(placement: .topBarTrailing) {
                Button("Save") {
                    note.title = editedTitle
                    note.content = editedContent
                    dismiss()
                }
            }
        }
    }
}

Upvotes: 1

Views: 277

Answers (1)

Benzy Neez
Benzy Neez

Reputation: 20459

I would think you want to supply your editor with a Note to be edited, then the supplied note is either updated or left unchanged when the view is dismissed, depending on which button was pressed. So the following changes are needed:

  • Your editor view also contains a NavigationStack. The navigation probably needs to be done outside of the editor, in other words, by a parent view.
  • The note with the initial content needs to be supplied by the parent view to the editor as a Binding.
  • To launch the editor, you probably want an edit button in the parent view.
  • Inside the editor, you don't need a separate State variable for every property of a Note. You just need a temporary copy of the whole Note.
  • The toolbar needs to be attached to the VStack of the editor, not to the NavigationStack.
  • When it comes to layout, start by letting the views find their natural sizes, then add width and height constraints and padding where needed. A TextEditor is greedy and uses all the space available, so you don't need to give it maximum width or maximum height.

Here is a revised version of your example with the changes applied:

struct Note {
    var title: String
    var content: String
}

struct EditNoteView: View {
    @Binding private var note: Note
    @State private var editedNote: Note
    @Environment(\.dismiss) private var dismiss

    init(note: Binding<Note>) {
        self._note = note
        self._editedNote = .init(initialValue: note.wrappedValue)
    }

    var body: some View {
        VStack(alignment: .leading) {
            TextEditor(text: $editedNote.title)
                .font(.title)
                .fontWeight(.bold)
                .frame(height: 50)
            Divider()
            TextEditor(text: $editedNote.content)
                .font(.body)
        }
        .padding(.horizontal, 12)
        .toolbar {
            ToolbarItem(placement: .topBarLeading) {
                Button("Cancel") {
                    dismiss()
                }
            }
            ToolbarItem(placement: .topBarTrailing) {
                Button("Save") {
                    note = editedNote
                    dismiss()
                }
            }
        }
    }
}

struct ContentView: View {
    @State private var note = Note(title: "Initial title", content: "Initial content")
    var body: some View {
        NavigationStack {
            VStack {
                VStack(alignment: .leading) {
                    Text(note.title)
                        .font(.title.bold())
                        .padding(.bottom)
                    Text(note.content)
                }
                .padding()
                .border(.gray, width: 2)
                .padding()

                NavigationLink {
                    EditNoteView(note: $note)
                        .navigationBarBackButtonHidden(true)
                } label: {
                    Label("Edit", systemImage: "pencil")
                }
            }
        }
    }
}

Hope that helps.

Upvotes: 2

Related Questions