GO FURTHER, FASTER: Try the Swift Career Accelerator today! >>

SOLVED: Connecting a datePicker's date to a TextEditor

Forums > SwiftUI

Hi there,

I have a datePicker and a TextEditor in a VStack. Now I want to be able to select a date, then edit and save text for this date. By chosing another date there should be a placeholder-text (if there hasn't been an entry yet) or the text I wrote sometime before.

What is the best way to do that?

Can I store the @State var date = Date() together with text?

On the long run, I want to store it permanantly, so I think I'll have to use CoreDate for it(?). But for now I would like to learn the connection between the datePicker and the TextEditor. Or is CoreData the way to go in the first place?

Thank you. Stam (on Xcode 12.5.1)

3      

Why you need TextEditor to edit dates, you can formate dates by using dateFormatter. In dateFormatter computed property you can edit it.

struct ContentView: View {
    @State private var date = Date()
    var body: some View {
        VStack{
            DatePicker(
                 "Start Date",
                 selection: $date,
                 displayedComponents: [.date]
             )

                Text("\(dateFormatter.string(from: self.date))")
        }
    }
    var dateFormatter: DateFormatter {

        let formatter = DateFormatter()
        formatter.dateFormat = "HH:mm E, d MMM y"
        formatter.dateStyle = .medium
        formatter.timeStyle = .medium
        return formatter
    }

}

3      

Thank you for the dateFormatter, this will come in handy later I think.

I do not want to edit dates, I want to save a note with the date of that day. By changing the date via the DatePicker, I want the note in TextEditor change to the note for this day, so I can reread and edit my notes.

struct ContentView: View {

@State private var textEditorText:String = "Write down your thoughts"
@State var date = Date()

var body: some View {
    VStack {
             DatePicker("", selection: $date, displayedComponents: .date)
             .datePickerStyle(CompactDatePickerStyle())
             .labelsHidden()

            Divider()

            TextField("", text: $textEditorText)
            .labelsHidden()
            .frame(width: 300, height: 300)
            .border(Color.black, width: 5)
            .background(Color.white)
            .cornerRadius(10)
 }
 }
 }

3      

Hacking with Swift is sponsored by RevenueCat.

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure and A/B test your entire paywall UI without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Ok you can save the date to coreDate directly by this way, you don't have to do this in TextField:

let newNote = Note(context: viewContext)
 newNote.date = Date() // This takes the date for that day and save it into coreData

which viewContext is your corData context. You have to create date attribute in your Note Entity with type Date

4      

Rather than having the DateFormatter as a computed property, which would mean a new one would be created every time it's referenced, it would be better to initialize it once and store it. This can be done as either:

  1. an immediately invoked closure:
let dateFormatter: DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateFormat = "HH:mm E, d MMM y"
    formatter.dateStyle = .medium
    formatter.timeStyle = .medium
    return formatter
}()

Or:

  1. Part of an initializer on the View:
let dateFormatter: DateFormatter

init() {
    dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "HH:mm E, d MMM y"
    dateFormatter.dateStyle = .medium
    dateFormatter.timeStyle = .medium
}

Also note that you don't need to use both dateFormat and dateStyle/timeStyle. Using both will just cause whichever one is applied last to be in effect. Which one you use depends on if you always want to display the datetime in a fixed format (use dateFormat) or if you want the datetime to be displayed in a localized format based on the user's current locale (use dateStyle/timeStyle).

4      

So, you are basically saying, CoreData is the way to go? Implemented saving which seems to work as wanted.

Next step is fetching and filtering all the notes to only show the ones specific for the day. And listing them in the TextEditor. Great, lets try. Any tips?

3      

I think you want somthing like this, try this code:

import SwiftUI

struct ContentView: View {
  @StateObject var vm = ViewModel()
    @FetchRequest(
        // To sort notes by date
        sortDescriptors: [NSSortDescriptor(keyPath: \Note.date, ascending: true)],
        animation: .default)
     var notes : FetchedResults<Note> // Fetch notes

    var body: some View {
        VStack{
            DatePicker(
                 "Start Date",
                selection: $vm.date,
                displayedComponents: [.date,.hourAndMinute]
            ).datePickerStyle(WheelDatePickerStyle())

            TextField(" type your note here ...", text: $vm.note)
                .textFieldStyle(RoundedBorderTextFieldStyle())
            Text(vm.dateFormatter.string(from: vm.date))
            Button("save"){
                vm.save()
            }
            List{
                ForEach(notes){ note in
                    VStack(alignment: .leading){
                        Text(note.note ?? "")
                        Text("\(note.date!, formatter: vm.dateFormatter)")
                }
            }.onDelete(perform: removeNote)
            }
        }.padding()
    }

    func removeNote(offsets: IndexSet) {
       withAnimation {
           offsets.map { notes[$0] }.forEach(vm.viewContext.delete)

           do {
               try vm.viewContext.save()
               //
           } catch {
               let nsError = error as NSError
               fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
           }
       }
   }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

class ViewModel: ObservableObject{
    var viewContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    @Published  var date = Date()
    @Published var note = ""

    var dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "HH:mm E, d MMM y"
        formatter.dateStyle = .medium
        formatter.timeStyle = .short
        return formatter
    }()

    func save(){
        let newNote = Note(context: viewContext)
        newNote.note = note
        newNote.date = date
        do{
            try viewContext.save()
        }catch{print("Error in creating new note")}
    }
}

3      

well, I couldn't try it, because of some errors I couldn't resolve. AppDelegate wasn't found, this seems to cause other context-errors.

But my setup is pretty similar, I think. I haven't made a viewModel yet, instead I have an @Environment-Object. Saving, fetching and presenting the notes in the List works.

How do I filter them, so only notes of the selected date are shown? I haven't figured out the correct predicate-format.

    import SwiftUI

    struct DailyNotesView: View {

        @Environment(\.managedObjectContext) var moc

        @FetchRequest(
            entity: Note.entity(),
            sortDescriptors: [NSSortDescriptor(keyPath: \Note.timestamp, ascending: true)]

 //        ,predicate: NSPredicate(format: "timestamp == date")

        )   var notes: FetchedResults<Note>

        @State private var textEditorText = "Hier eingeben"
        @State var date = Date()

        var body: some View {
            VStack {

                DatePicker("", selection: $date, displayedComponents: .date)
                    .datePickerStyle(CompactDatePickerStyle())
                    .labelsHidden()

                TextEditor(text: $textEditorText)
                    .labelsHidden()
                    .frame(width: 300, height: 300)
                    .colorMultiply(.gray)
                    .cornerRadius(10)

                Button(action: {
                    let newNote = Note(context: self.moc)

                    newNote.note = self.textEditorText
                    newNote.timestamp = self.date

                    try? self.moc.save()
                    print("- note saved: \(textEditorText)")
                    print("- date saved: \(date)")
                }, label: {
                    Text("save note")
                })
                List {
                    ForEach(notes) { note in
                        Text(note.note ?? "Error")
                    }
                }.frame(width: 300, height: 300)
                .colorMultiply(.gray)
                .cornerRadius(10)
            }
            .ignoresSafeArea()
        }

    }

    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            DailyNotesView()
                .previewDevice("iPhone 12 Pro")
        }
    }

3      

This workes for me:

ForEach(notes.filter{$0.timestamp == date}) { note in

4      

Great! This works for me, too. Of cause there will pop up a lot of other difficulties and questions going on from here, but I think thats all for this topic.

Thank you for helping me out.

3      

Hacking with Swift is sponsored by RevenueCat.

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure and A/B test your entire paywall UI without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.