首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >将选中/未检查的任务保存到UserDefaults

将选中/未检查的任务保存到UserDefaults
EN

Stack Overflow用户
提问于 2022-09-10 23:27:33
回答 1查看 58关注 0票数 0

如何保存“选中/未检查的任务”以使用UserDefaults持久化数据?

在我的ListViewModel,中,我试图将检查点Bool isCompleted状态保存到UserDefaults。当您重新启动应用程序时,复选标记将重置为false。

不确定是否需要将EnvironmentObject或ObservableObject添加到ListView

ListViewModel.swift

代码语言:javascript
运行
复制
//
//  ListViewModel.swift
//

import Foundation

class ListViewModel: ObservableObject {
    
    @Published var items: [ItemModel] = [] {
        didSet {
            saveItems()
        }
    }
    
    let itemsKey: String = "task_items_list"
    
    init() {
        myTaskItems()
    }
    
    func myTaskItems() {
        let taskItems = [
            ItemModel(title: "This is the first task!", isCompleted: false),
            ItemModel(title: "This is the second task!", isCompleted: false),
            ItemModel(title: "This is the third task!", isCompleted: false),
            ItemModel(title: "This is the fourth task!", isCompleted: false),
            ItemModel(title: "This is the fifth task!", isCompleted: false)
        ]
        items.append(contentsOf: taskItems)
        
        guard
            let data = UserDefaults.standard.data(forKey: itemsKey),
            let savedTaskItems = try? JSONDecoder().decode([ItemModel].self, from: data)
        else { return }
        
        self.items = savedTaskItems
    }
    // Update TaskdItems Toggle
    func updateItem(item:ItemModel) {
        if let index = items.firstIndex(where: { $0.id == item.id }) {
            items[index] = item.updateCompletion()
        }
    }
    // Save to UserDefaults
    func saveItems() {
        if let encodedData = try? JSONEncoder().encode(items) {
            UserDefaults.standard.set(encodedData, forKey: itemsKey)
        }
    }
}

ItemModel.swift

代码语言:javascript
运行
复制
//  ItemModel.swift
//

import Foundation

struct ItemModel:Identifiable, Codable {
    let id: String
    let title: String
    let isCompleted: Bool
    
    init(id: String = UUID().uuidString, title: String, isCompleted: Bool) {
        self.id = UUID().uuidString
        self.title = title
        self.isCompleted = isCompleted
    }
    
    func updateCompletion() -> ItemModel {
        return ItemModel(id: id, title: title, isCompleted: !isCompleted)
    }
    
}

ListView.swift

代码语言:javascript
运行
复制
//
//  ListView.swift
//

import SwiftUI

struct ListView: View {
    
    @EnvironmentObject var listViewModel: ListViewModel
    
    var body: some View {
        List {
            ForEach(listViewModel.items) { item in
                ListRowView(item: item)
                    .onTapGesture {
                        listViewModel.updateItem(item: item)
                    }
            }
        }
        .listStyle(PlainListStyle())
        .navigationTitle("My Task List")
    }
    
    
    struct ListView_Previews: PreviewProvider {
        static var previews: some View {
            NavigationView {
                ListView()
            }
            .environmentObject(ListViewModel())
        }
    }
    
}

ListRowView.swift

代码语言:javascript
运行
复制
//
//  ListRowView.swift
//

import SwiftUI

struct ListRowView: View {
    
    let item: ItemModel
    
    var body: some View {
        HStack {
            Image(systemName: item.isCompleted ? "checkmark.circle" : "circle")
                .foregroundColor(item.isCompleted ? .green : .gray)
            Text(item.title)
            Spacer()
        }
        .font(.title2)
        .padding(.vertical, 8)
    }
}

struct ListRowView_Previews: PreviewProvider {
    
    static var item1 = ItemModel(title: "First item!", isCompleted: false)
    static var item2 = ItemModel(title: "Second item!", isCompleted: true)
    
    static var previews: some View {
        
        Group {
            ListRowView(item: item1)
            ListRowView(item: item2)
        }
        .previewLayout(.sizeThatFits)
    }
}

TaskListApp.swift

代码语言:javascript
运行
复制
//
//  TaskListApp.swift
//

import SwiftUI

@main
struct TaskListApp: App {

    @StateObject var listViewModel: ListViewModel = ListViewModel()

    var body: some Scene {
        WindowGroup {
            NavigationView {
                ListView()
            }
            .environmentObject(listViewModel)
        }
    }
}

EN

回答 1

Stack Overflow用户

发布于 2022-09-11 00:29:26

这里的问题是您的myTask函数。每次初始化ListViewmodel时,都是使用初始数组覆盖UserDefaults。将该代码移动到else语句块中。

代码语言:javascript
运行
复制
func myTaskItems() {        
    guard
        let data = UserDefaults.standard.data(forKey: itemsKey),
        let savedTaskItems = try? JSONDecoder().decode([ItemModel].self, from: data)
    else {
        let taskItems = [
            ItemModel(title: "This is the first task!", isCompleted: false),
            ItemModel(title: "This is the second task!", isCompleted: false),
            ItemModel(title: "This is the third task!", isCompleted: false),
            ItemModel(title: "This is the fourth task!", isCompleted: false),
            ItemModel(title: "This is the fifth task!", isCompleted: false)
        ]
        items.append(contentsOf: taskItems)
        return
    }

这确实解决了这个问题。

但是,我想请您考虑一种不同的方法,使您的代码更容易。您可以结合使用@Appstorage@Binding。在视图模型中使用@Appstorage,并通过BindingItemsModel传递给ListRowView

我已经对代码进行了评论,因为我认为它可能会有所帮助。如果您对此实现有疑问,请随意提问。

代码语言:javascript
运行
复制
//needed to use array in @Appstorage
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
        }
    
}

class ListViewModel: ObservableObject {
    // this loads and saves the items automatic to UserDefaults
    @AppStorage("task_items_list") var items: [ItemModel] = []
    
    
    init() {
        //fill the collection only if it is empty
        if items.isEmpty{
            myTaskItems()
        }
    }
    
    func myTaskItems() {
        items = [
            ItemModel(title: "This is the first task!", isCompleted: false),
            ItemModel(title: "This is the second task!", isCompleted: false),
            ItemModel(title: "This is the third task!", isCompleted: false),
            ItemModel(title: "This is the fourth task!", isCompleted: false),
            ItemModel(title: "This is the fifth task!", isCompleted: false)
        ]
        
    }
    
    //The rest of the functions are not needed
}
        

struct ItemModel: Identifiable, Codable {
    var id: String = UUID().uuidString
    var title: String
    var isCompleted: Bool
}

struct ListView: View {
    
    @EnvironmentObject var listViewModel: ListViewModel
    
    var body: some View {
        List {
            //Use the $ syntax to pass a binding on to the subviews
            ForEach($listViewModel.items) { $item in
                ListRowView(item: $item)
            }
        }
        .listStyle(PlainListStyle())
        .navigationTitle("My Task List")
    }
}

struct ListRowView: View {
    //this binding will assure changes will bubble up into the viewmodel and will be saved in UserDefaults
    @Binding var item: ItemModel
    
    var body: some View {
        HStack {
            Image(systemName: item.isCompleted ? "checkmark.circle" : "circle")
                .foregroundColor(item.isCompleted ? .green : .gray)
            Text(item.title)
            Spacer()
        }
        .font(.title2)
        .padding(.vertical, 8)
        // this is enough to toggle the checkmark
        .onTapGesture {
            item.isCompleted.toggle()
        }
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73675830

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档