从不同的视图更新数组的内容 [英] Updating the contents of an array from a different view

查看:26
本文介绍了从不同的视图更新数组的内容的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm writing a macOS app in Swiftui, for Big Sur and newer. It's a three pane navigationview app, where the left most pane has the list of options (All Notes in this case), the middle pane is a list of the actual items (title and date), and the last one is a TextEditor where the user adds text.

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

Each pane is a view that calls the the next view via a NavigationLink. Here's the basic code for that.

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 中,我如何从其他视图更新它?

When I create a new note, how can I save the text the user added on the TextEditor in NoteView in the array loaded in AllNotes so I could save the new text? Ideally there is a SaveNote() function that would happen on TextEditor .onChange. But again, given that the array lives in AllNotes, how can I update it from other views?

感谢您的帮助.新手来了!

Thanks for the help. Newbie here!

推荐答案

在应用中使用EnvironmentObject

use EnvironmentObject in App

import SwiftUI

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

现在DataModel是一个符合ObservableObject的类

now DataModel is a class conforming to ObservableObject

import SwiftUI

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

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

any data related stuff should be done in DataModel not in View, plus you can access it and update it from anywhere, declare it like this in your ContentView or any child View

笔记视图

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),因此您需要添加此扩展以使其工作.

AppStorage is the better way to use UserDefaults but AppStorage does not work with custom Objects yet (I think it does for iOS 15), so you need to add this extension to make it work.

导入 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 视图以处理新更改

Now I changed AllNotes view to work with new changes

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")
                }
            }
        }
    }
}

这篇关于从不同的视图更新数组的内容的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆