我试图弄清楚如何使两件事一起工作。第一个是使用 userdefaults 的持久性数据,我通过使用 @Published 和 @Observable 弄清楚了这一点,然后使用 JSONencoder/decoder 来获取要保存的数据,即使在应用程序关闭并重新打开时也是如此。我最近也通过上一个问题了解了第二个问题
我的问题是将两者结合起来。我似乎无法弄清楚如何将下面的代码与预定义的数据数组一起使用,并使用 UserDefaults 使更改持续存在。任何帮助将不胜感激。也许我正在错误地处理这个问题。
通过 UserDefaults 获取持久数据的代码
import SwiftUI
struct CharacterModel: Identifiable, Codable {
var id = UUID()
var name: String
var level: Int
}
class CharacterViewModel: ObservableObject {
@Published var characters = [CharacterModel]() {
// Write data back to Model
didSet {
let encoder = JSONEncoder()
if let encoded = try?
encoder.encode(characters) {
UserDefaults.standard.set(encoded, forKey: "Characters")
}
}
}
init() {
if let characters = UserDefaults.standard.data(forKey: "Characters") {
let decoder = JSONDecoder()
if let decoded = try?
decoder.decode([CharacterModel].self, from: characters) {
self.characters = decoded
return
}
}
self.characters = []
}
}
struct DetailView: View {
@Environment(\.presentationMode) var presentationMode
@ObservedObject var characterVM: CharacterViewModel
@State private var name = ""
@State private var level = ""
var body: some View {
NavigationView {
Form {
TextField("Name", text: $name)
TextField("Level", text: $level)
.keyboardType(.numberPad)
}
.navigationBarTitle("Add Level")
.navigationBarItems(trailing: Button("Save") {
if let actualLevel = Int(self.level) {
let character = CharacterModel(name: self.name, level: actualLevel)
self.characterVM.characters.append(character)
self.presentationMode.wrappedValue.dismiss()
}
})
}
}
}
struct DetailView_Previews: PreviewProvider {
static var previews: some View {
DetailView(characterVM: CharacterViewModel())
}
}
struct Home: View {
@State private var showingAddCharacter = false
@State var selectedCharacter : CharacterModel!
@ObservedObject var characterVM = CharacterViewModel()
var body: some View {
NavigationView {
List {
ForEach(characterVM.characters) { char in
HStack {
VStack(alignment: .leading) {
Text(char.name)
.font(.headline)
}
Spacer()
Text("\(char.level)")
}
}
.onDelete(perform: removeItems)
}
.navigationBarTitle("Characters")
.navigationBarItems(trailing:
Button(action: {
self.showingAddCharacter = true
}) {
Image(systemName: "plus")
}
)
.sheet(isPresented: $showingAddCharacter) {
DetailView(characterVM: self.characterVM)
}
}
}
func removeItems(at offsets: IndexSet) {
characterVM.characters.remove(atOffsets: offsets)
}
}
struct Home_Previews: PreviewProvider {
static var previews: some View {
Home()
}
}
编辑预定义数组
class Training: ObservableObject, Identifiable {
let id: String
@Published var trainingName: String
@Published var isRequired: Bool
init(id: String, trainingName: String, isRequired: Bool) {
self.id = id
self.trainingName = trainingName
self.isRequired = isRequired
}
}
class GetTrainings: ObservableObject {
@Published var items = [Training]()
init() {
self.items = [
Training(id: "ttt1", trainingName: "Safety", isRequired: true),
Training(id: "ttt2", trainingName: "Administrative", isRequired: false),
Training(id: "ttt3", trainingName: "Computer", isRequired: true),
Training(id: "ttt4", trainingName: "People", isRequired: true),
Training(id: "ttt5", trainingName: "Managerial", isRequired: true),
]
}
}
struct TrainingList: View {
@ObservedObject var trainings = GetTrainings()
var body: some View {
NavigationView {
VStack {
List {
ForEach(trainings.items) { training in
HStack {
NavigationLink(destination: TrainingView(training: training)) {
Text("\(training.trainingName)")
}
}
}
}
}.navigationBarTitle("Training List")
.onAppear {
self.trainings.objectWillChange.send() // refresh
}
}
}
}
struct TrainingView: View {
@ObservedObject var training: Training
var body: some View {
VStack {
Text("\(training.trainingName)").font(.body)
Text("\(training.isRequired == true ? "Required Training" : "Training Not Required")")
HStack {
NavigationLink(destination: EditTraining(training: training)) {
Text("Edit Training Details")
}
}
}.navigationBarTitle("\(training.trainingName) Page", displayMode: .inline)
}
}
struct EditTraining: View {
@ObservedObject var training: Training
@State private var newName: String
@State private var isRequiredTraining: Bool
init(training: Training) {
self.training = training
self._newName = State(initialValue: training.trainingName)
self._isRequiredTraining = State(initialValue: training.isRequired)
}
private func submitData() {
let newName = self.newName
let newBoolVal = self.isRequiredTraining
print("Firebase Sync Id is :\(training.id) Text: \(newName) and Bool: \(newBoolVal)")
self.training.trainingName = newName
self.training.isRequired = newBoolVal
}
var body: some View {
VStack {
Form {
Section (header: Text("Edit")) {
Text("\(training.trainingName)")
/* TextField should Populate With passed In Training Name Here*/
TextField("New Name", text: self.$newName)
Toggle(isOn: self.$isRequiredTraining) {
Text("Is Required")
}
}
Section {
Button(action: {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder),
to:nil, from:nil, for:nil)
self.submitData()
}) {
Text("Submit")
}
}
}
}.navigationBarTitle("Edit Training Page", displayMode: .inline)
}
}