I'm trying to make something similar to the iOS notes app but for journaling; basically, I want there to be a list of journal entry cells users can scroll through which each display a detail view after they're clicked on where the user can view and edit their journal entry. The updating works fine, the only issue is that JournalDetailView
dismisses itself after updateEntry()
is called (after the user taps the "Done" button). I'm guessing this is because updateEntry()
forces the view to reload, but I'm not sure how to get around this.
Here's the model:
struct JournalEntry: Identifiable, Hashable, Codable {
@DocumentID var id: String? = UUID().uuidString
@ServerTimestamp var date: Timestamp?
var text: String
var userId: String?
}
Here's the view code:
struct JournalCellView: View {
@ObservedObject var vm: JournalViewModel
@Binding var addButtonTapped: Bool
@State var showDetail = false
@State var entry: JournalEntry
var body: some View {
NavigationLink(destination: JournalDetailView(vm: vm, entry: $entry, text: entry.text), isActive: $showDetail, label: {
VStack {
HStack {
Text(entry.date!.dateValue(), style: .date)
.fontWeight(.bold)
.font(.system(size: 18))
.foregroundColor(.black)
.padding(.bottom, 3)
Spacer()
}
HStack {
Text(entry.text)
.font(.system(size: 14))
.foregroundColor(.secondary)
.lineLimit(1)
Spacer()
}
}.padding()
.background(RoundedRectangle(cornerRadius: 18).foregroundColor(.white))
.padding(.vertical, 4)
.onTapGesture {
showDetail = true
}
.onAppear {
if addButtonTapped {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
showDetail = true
}
addButtonTapped = false
}
}
})
}
}
struct JournalDetailView: View {
@Environment(\.presentationMode) var presentationMode
@ObservedObject var vm: JournalViewModel
@Binding var entry: JournalEntry
@State var text: String
@State var isTyping = false
var body: some View {
VStack {
HStack {
Button(action: {
presentationMode.wrappedValue.dismiss()
}, label: { Image(systemName: "chevron.left").foregroundColor(.burgundy) })
Spacer()
if isTyping {
Button(action: {
endEditing()
updateEntry()
isTyping = false
}) {
Text("Done")
.foregroundColor(.burgundy)
}
} else {
Text("")
}
}.padding(.vertical)
Text(entry.date!.dateValue(), style: .date)
TextEditor(text: $text)
.onTapGesture {
isTyping = true
}
Spacer()
}.padding()
.navigationBarHidden(true)
}
func updateEntry() {
vm.updateJournalEntry(docID: entry.id!, date: entry.date!, text: text)
}
}
Here's updateJournalEntry()
:
func updateJournalEntry(docID: String, date: Timestamp, text: String) {
db.collection("journals").document(docID
).updateData(["date": date, "text": text, "userId": Auth.auth().currentUser!.uid])
}
I managed to get around this by only updating after the view would be dismissed naturally using .onDisappear
and .onReceive
. Not the cleanest solution, but it works. If someone has another suggestion, please contribute!
.onDisappear {
updateEntry()
}
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)) { _ in
updateEntry()
}