从不同的视图更新数组的内容

Updating the contents of an array from a different view

我正在使用 Swiftui 为 Big Sur 和更新版本编写 macOS 应用程序。这是一个三窗格导航视图应用程序,其中最左侧的窗格具有选项列表(在本例中为所有注释),中间窗格是实际项目的列表(标题和日期),最后一个是文本编辑器,其中用户添加文本。

每个窗格都是一个视图,通过 NavigationLink 调用下一个视图。这是基本代码。

struct NoteItem: Codable, Hashable, Identifiable {
    let id: Int
    var text: String
    var date = Date()
    var dateText: String {
        dateFormatter.dateFormat = "EEEE, MMM d yyyy, h:mm a"
        return dateFormatter.string(from: date)
    }
    var tags: [String] = []
}


struct ContentView: View {
    @State var selection: Set<Int> = [0]
    var body: some View {
        NavigationView {
            
            List(selection: self.$selection) {
                NavigationLink(destination: AllNotes()) {
                    Label("All Notes", systemImage: "doc.plaintext")
                }
                .tag(0)
            }
            .listStyle(SidebarListStyle())
            .frame(minWidth: 100, idealWidth: 150, maxWidth: 200, maxHeight: .infinity)
            
            Text("Select a note...")
                .frame(maxWidth: .infinity, maxHeight: .infinity)
        }
    }
}

struct AllNotes: View {

    @State var items: [NoteItem] = {
        guard let data = UserDefaults.standard.data(forKey: "notes") else { return [] }
        if let json = try? JSONDecoder().decode([NoteItem].self, from: data) {
            return json
        }
        return []
    }()
    
    @State var noteText: String = ""

    var body: some View {
       NavigationView {
         List(items) { item in
                NavigationLink(destination: NoteView()) {
                    VStack(alignment: .leading) {
                        Text(item.text.components(separatedBy: NSCharacterSet.newlines).first!)
                        Text(item.dateText).font(.body).fontWeight(.light)
                    }
                    .padding(.vertical, 8)
                }
            }
            .listStyle(InsetListStyle())

            Text("Select a note...")
                .frame(maxWidth: .infinity, maxHeight: .infinity)
       }
    }
    .navigationTitle("A title")
    .toolbar {
        ToolbarItem(placement: .navigation) {
                Button(action: {
                    NewNote()
                }) {
                    Image(systemName: "square.and.pencil")
                }
         }
    }

}

struct NoteView: View {
    @State var text: String = ""
    var body: some View {
        HStack {
            VStack(alignment: .leading) {
                TextEditor(text: $text).padding().font(.body)
                    .onChange(of: text, perform: { value in
                            print("Value of text modified to = \(text)")
                        })
                Spacer()
            }
            Spacer()
        }
        .padding()
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(Color.white)
    }
}

创建新笔记时,如何将用户在 NoteView 中的 TextEditor 中添加的文本保存在 AllNotes 中加载的数组中,以便保存新笔记文本?理想情况下,有一个 SaveNote() 函数会在 TextEditor .onChange 上发生。但是同样,鉴于数组位于 AllNotes,我如何从其他视图更新它?

感谢您的帮助。这里是新手!

在 App 中使用 EnvironmentObject

import SwiftUI

@main
struct NotesApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(DataModel())
        }
    }
}

现在 DataModel 是一个 class 符合 ObservableObject

import SwiftUI

final class DataModel: ObservableObject {
    @AppStorage("notes") public var notes: [NoteItem] = []
}

任何与数据相关的事情都应该在 DataModel 而不是视图中完成,而且您可以从任何地方访问它并更新它,在您的 ContentView 或任何子视图中这样声明它

记事本

import SwiftUI

struct NoteView: View {
    
    @EnvironmentObject private var data: DataModel
    var note: NoteItem
    
    @State var text: String = ""
    var body: some View {
        HStack {
            VStack(alignment: .leading) {
                TextEditor(text: $text).padding().font(.body)
                    .onChange(of: text, perform: { value in
                        guard let index =     data.notes.firstIndex(of: note) else { return }
                        data.notes[index].text = value
                    })
                Spacer()
            }
            Spacer()
        }
        .padding()
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(Color.white)
        .onAppear() {
            print(data.notes.count)
        }
    }
}

AppStorage 是使用 UserDefaults 的更好方法,但 AppStorage 还不能使用自定义对象(我认为它适用于 iOS 15),因此您需要添加此扩展以使其工作。

导入 SwiftUI

struct NoteItem: Codable, Hashable, Identifiable {
    let id: UUID
    var text: String
    var date = Date()
    var dateText: String {
        let df = DateFormatter()
        df.dateFormat = "EEEE, MMM d yyyy, h:mm a"
        return df.string(from: date)
    }
    var tags: [String] = []
}

extension Array: RawRepresentable where Element: Codable {
    public init?(rawValue: String) {
        guard let data = rawValue.data(using: .utf8),
              let result = try? JSONDecoder().decode([Element].self, from: data)
        else {
            return nil
        }
        self = result
    }
    
    public var rawValue: String {
        guard let data = try? JSONEncoder().encode(self),
              let result = String(data: data, encoding: .utf8)
        else {
            return "[]"
        }
        return result
    }
}

现在我更改了 AllNotes 视图以处理新的更改

struct AllNotes: View {
    
    @EnvironmentObject private var data: DataModel
    
    @State var noteText: String = ""
    
    var body: some View {
        NavigationView {
            List(data.notes) { note in
                NavigationLink(destination: NoteView(note: note)) {
                    VStack(alignment: .leading) {
                        Text(note.text.components(separatedBy: NSCharacterSet.newlines).first!)
                        Text(note.dateText).font(.body).fontWeight(.light)
                    }
                    .padding(.vertical, 8)
                }
            }
            .listStyle(InsetListStyle())
            
            Text("Select a note...")
                .frame(maxWidth: .infinity, maxHeight: .infinity)
        }
        .navigationTitle("A title")
        .toolbar {
            ToolbarItem(placement: .navigation) {
                Button(action: {
                    data.notes.append(NoteItem(id: UUID(), text: "New Note", date: Date(), tags: []))
                }) {
                    Image(systemName: "square.and.pencil")
                }
            }
        }
    }
}