swift练习--列表

1.最终效果

截屏2025-02-28 14.42.09.png

截屏2025-02-28 14.42.16.png

截屏2025-02-28 14.42.20.png

2.数据方面

数据方面包括:数据模型(使用结构体表示)、数据持久化、数据管理

2.1数据模型

/ 定义数据模型
//struct ListItem: Identifiable, Codable {
//    var id = UUID()
//    let title: String
//    let content: String
//    let date: Date
//}
struct ListItem: Identifiable, Codable {
    let id: UUID  // 移除默认值
    let title: String
    let content: String
    let date: Date
    
    // 双用途初始化器
    init(
        id: UUID = UUID(), // 默认生成新ID
        title: String,
        content: String,
        date: Date = Date() // 默认使用当前时间
    ) {
        self.id = id
        self.title = title
        self.content = content
        self.date = date
    }
}

使用带有初始值的不可变属性(let id = UUID())在解码时不会被覆盖,因为初始化后的值无法改变。这意味着当从JSON或其他数据格式解码时,id会被初始化为默认的UUID(),而不是解码出来的值。

Codable需要能够编码和解码所有存储属性。当使用let声明id并赋予初始值时,解码器无法修改这个属性,因为它是一个常量。因此,当用户尝试从持久化存储(如UserDefaults)加载数据时,id实际上会被重新生成,而不是恢复之前保存的值,这会导致数据不一致的问题。

使用let,这是基于Identifiable协议和SwiftUI的要求,需要id稳定不变。但这里的问题在于Codable的解码过程需要能够设置id的值,而使用let阻止了这一点,导致冲突。

结构体在遵循Codable协议时,编译器会自动生成一个解码初始化器,但如果存在常量属性(let)并且有默认值,编译器可能无法正确合成初始化器。因此,需要手动实现解码的初始化器,以便在初始化时为id赋值。

init中给出类型和等于一个函数的区别是一个是必填,一个是可选。

🧩 参数默认值设计差异解析

ListItem 的初始化器中,idtitle 参数的设计差异源于它们的业务属性和数据特性:

init(
    id: UUID = UUID(),    // 自动生成唯一标识
    title: String,        // 必须显式提供
    content: String,      // 必须显式提供
    date: Date = Date()   // 自动记录当前时间
)

📌 核心差异对照表

参数 默认值 必需性 数据来源 设计意图
id ✅ UUID() 可选 系统自动生成 保证唯一性,避免人工干预
title ❌ 无 必填 用户输入 强制有效数据输入,避免空内容
date ✅ Date() 可选 系统自动获取 准确记录创建时间,简化用户操作
content ❌ 无 必填 用户输入 确保内容完整性,符合业务逻辑要求

// 数据管理类
class ListData: ObservableObject {
    @Published var items: [ListItem] = []
    
    init() {
        loadItems()
    }
    
    // 从UserDefaults加载数据
    func loadItems() {
        if let data = UserDefaults.standard.data(forKey: "items") {
            if let decoded = try? JSONDecoder().decode([ListItem].self, from: data) {
                items = decoded
            }
        }
    }
    
    // 保存数据到UserDefaults
    func saveItems() {
        if let encoded = try? JSONEncoder().encode(items) {
            UserDefaults.standard.set(encoded, forKey: "items")
        }
    }
    
    // 添加新项目
    func addItem(title: String, content: String) {
        let newItem = ListItem(title: title, content: content, date: Date())
        items.insert(newItem, at: 0)
        saveItems()
    }
    
    // 删除项目
    func deleteItem(at offsets: IndexSet) {
        items.remove(atOffsets: offsets)
        saveItems()
    }
}

这里面UserDefaults.standard是轻量级数据存储的类。
UserDefaults.standard.data(forKey: "items")里的.data指获取的是Data类型数据,即二进制数据,

let user = User(name: "Alice", age: 30)
if let data = try? JSONEncoder().encode(user) {
    UserDefaults.standard.set(data, forKey: "user")
}

这个Data数据是通过JSONEncoder处理过的二进制数据,上面是处理例子。

3.视图方面

// 主界面
struct ContentView: View {
    @StateObject var listData = ListData()
    @State private var showingAddView = false
    
    var body: some View {
        NavigationStack {
            List {
                ForEach(listData.items) { item in
                    VStack(alignment: .leading, spacing: 8) {
                        HStack {
                            Text(item.title)
                                .font(.headline)
                            Spacer()
                            Text(item.date, style: .date)
                                .font(.caption)
                                .foregroundColor(.gray)
                        }
                        Text(item.content)
                            .font(.subheadline)
                            .foregroundColor(.secondary)
                            .lineLimit(2)
                    }
                    .padding(.vertical, 8)
                }
                .onDelete(perform: listData.deleteItem)
            }
            .navigationTitle("事项列表")
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button {
                        showingAddView = true
                    } label: {
                        Image(systemName: "plus")
                    }
                }
            }
            .sheet(isPresented: $showingAddView) {
                AddItemView(listData: listData)
            }
        }
    }
}

// 添加新项目界面
struct AddItemView: View {
    @ObservedObject var listData: ListData
    @Environment(\.dismiss) var dismiss
    @State private var title = ""
    @State private var content = ""
    
    var body: some View {
        NavigationStack {
            Form {
                Section(header: Text("基本信息")) {
                    TextField("标题", text: $title)
                    TextField("内容", text: $content)
                }
            }
            .navigationTitle("新建事项")
            .toolbar {
                ToolbarItem(placement: .cancellationAction) {
                    Button("取消") {
                        dismiss()
                    }
                }
                ToolbarItem(placement: .confirmationAction) {
                    Button("保存") {
                        listData.addItem(title: title, content: content)
                        dismiss()
                    }
                    .disabled(title.isEmpty || content.isEmpty)
                }
            }
        }
    }
}

// 预览
#Preview {
    ContentView()
}

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容