SwiftUI View Pop Out Itself When Updating Content

Viewed 31

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
        }
    }
}
0 Answers
Related