I'm trying to build a note-taking tool with SwiftUI and it's behaving horribly. For some reason, after navigating to the note editor with NavigationLink, this view disappears whenever I use the editor.
I'm well aware that I'm a newbie and I'm probably doing something very basic wrong, which is most likely not a SwiftUI bug. I've searched for information for a day and still can't slove the problem is a clear hint. Please do let me know if you know the cause of the problem.
ContentView
The home page of this application, the page the user sees each time they open it, the root of the NavigationView. It get and display all months from DataManager, and use NavigationLink to navigate to MonthView.
struct ContentView: View {
@StateObject private var vm = ViewModel()
var body: some View {
NavigationView {
Form {
ForEach($vm.data) { $thisItem in
NavigationLink(destination: MonthView(month: thisItem.month)) {
MonthCellView(monthCover: thisItem)
}
}
}
}
.navigationViewStyle(.stack)
}
}
extension ContentView {
private class ViewModel: ObservableObject, DataManagerObserver {
private let dataManager = DataManager.instance
var dataManagerObserverID = UUID()
var data = [MonthCover]()
init() {
dataManager.addObserver(self)
self.data = dataManager.fetchAllMonthCovers()
}
func createMemoirItem() {
dataManager.createMemoirItem()
self.data = dataManager.fetchAllMonthCovers()
notifyObservers()
}
func dataManagerDidUpdateMemoirItems() {
self.data = dataManager.fetchAllMonthCovers()
notifyObservers()
}
func dataManagerDidUpdateMonthCovers() {
self.data = dataManager.fetchAllMonthCovers()
notifyObservers()
}
func notifyObservers() {
objectWillChange.send()
}
}
}
MonthView
Get all the MemoirItems for the month from the DataManager and use the Navigation Link to navigate to the editor.
struct MonthView: View {
let month: Month
@StateObject private var vm = ViewModel()
var body: some View {
NavigationLink(destination: EmptyView()) { EmptyView() }
Form {
ForEach(vm.data) { thisItem in
Section {
NavigationLink {
MemoirItemEditorView(memoirItem: thisItem)
} label: {
MemoirItemCoverView(item: thisItem)
}
}
}
}
.onAppear {
vm.fetchData(month: month)
}
}
}
extension MonthView {
class ViewModel: ObservableObject, DataManagerObserver {
let dataManager = DataManager.instance
var dataManagerObserverID = UUID()
var month = Month(year: 2000, month: 1)
var data = [MemoirItem]()
func fetchData(month: Month) {
self.month = month
self.data = dataManager.fetchMemoirItemForGivenTimePeriod(month: month)
dataManager.addObserver(self)
notifyObservers()
}
func dataManagerDidUpdateMonthCovers() {
self.data = dataManager.fetchMemoirItemForGivenTimePeriod(month: month)
objectWillChange.send()
}
}
}
EditorView:
Edit the content of the MemoirItem and use the DataManager to save the edited content each time it changes.
TextEditorViewWrapperView used in this view is a UIViewControllerRepresentable, a view similar to TextEditor in the SwiftUI world, what it does is just take a binding string as a parameter, edit it, and change it responsibly.
struct MemoirItemEditorView: View {
let memoirItem: MemoirItem
@StateObject private var vm: ViewModel
var body: some View {
TextEditorViewWrapperView(text: $vm.memoirItemContent)
.navigationBarTitleDisplayMode(.inline)
}
init(memoirItem: MemoirItem) {
self.memoirItem = memoirItem
self._vm = StateObject(wrappedValue: ViewModel(memoirItem))
}
}
extension MemoirItemEditorView {
private class ViewModel: ObservableObject {
let dataManager = DataManager.instance
var memoirItemID: UUID
var memoirItemContent: String {
didSet {
dataManager.updateMemoirItemContent(forID: memoirItemID, newContent: memoirItemContent)
}
}
var memoirItemContentWhenOpen = String()
init(_ memoirItem: MemoirItem) {
self.memoirItemID = memoirItem.id
self.memoirItemContent = memoirItem.content
self.memoirItemContentWhenOpen = memoirItem.content
}
}
}