SwiftUI实现条目按日期排序及数值累加功能遇阻求助
问题解决:条目按日期排序+数值累加修复
我用UserDefaults存储数据,现有Model和ViewModel代码如下:
import Foundation struct Item: Identifiable, Codable { var id = UUID() var name: String var int: Int var date = Date() } class ItemViewModel: ObservableObject { @Published var ItemList = [Item] () init() { load() } func load() { guard let data = UserDefaults.standard.data(forKey: "ItemList"), let savedItems = try? JSONDecoder().decode([Item].self, from: data) else { ItemList = []; return } ItemList = savedItems } func save() { do { let data = try JSONEncoder().encode(ItemList) UserDefaults.standard.set(data, forKey: "ItemList") } catch { print(error) } } }
View代码里想实现两个功能:添加新条目后按日期排序(尝试用.sorted()无效),以及将条目中的int值累加到totalNumber(自定义累加逻辑失效),View代码如下:
import SwiftUI struct ContentView: View { @State private var name = "" @State private var int = 0 @AppStorage("TOTAL_NUMBER") var totalNumber = 0 @StateObject var VM = ItemViewModel() var body: some View { VStack(spacing: 30) { VStack(alignment: .leading) { HStack { Text("Name:") TextField("Type Here...", text: $name) } HStack { Text("Number:") TextField("Type Here...", value: $int, formatter: NumberFormatter()) } Button { addItem() VM.save() name = "" int = 0 } label: { Text ("ADD PERSON") } } .padding() VStack(alignment: .leading) { List(VM.ItemList) { Item in Text(Item.name) Text("\(Item.int)") Text("\(Item.date, format: .dateTime.day().month().year())") } Text("\(totalNumber)") .padding() } } } func addItem() { VM.ItemList.append(Item(name: name, int: int)) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
一、修复按日期排序问题
方案1:View层直接排序
直接在List数据源中对数组排序,最新条目放最上面用降序,反之用升序:
// 降序排序(最新日期在前) List(VM.ItemList.sorted(by: { $0.date > $1.date })) { Item in Text(Item.name) Text("\(Item.int)") Text("\(Item.date, format: .dateTime.day().month().year())") }
方案2:ViewModel层统一维护排序(更规范)
把排序逻辑放到ViewModel里,确保添加、加载数据后数组始终有序,同时统一管理添加条目逻辑:
class ItemViewModel: ObservableObject { @Published var itemList = [Item]() // 变量名改成小写开头,符合Swift命名规范 init() { load() sortItems() } func load() { guard let data = UserDefaults.standard.data(forKey: "ItemList"), let savedItems = try? JSONDecoder().decode([Item].self, from: data) else { itemList = [] return } itemList = savedItems sortItems() } func save() { do { let data = try JSONEncoder().encode(itemList) UserDefaults.standard.set(data, forKey: "ItemList") } catch { print(error) } } // 把添加条目逻辑移到ViewModel func addItem(name: String, int: Int) { let newItem = Item(name: name, int: int) itemList.append(newItem) sortItems() save() // 这里直接保存,View里不用再调用 } // 私有排序方法 private func sortItems() { itemList.sort(by: { $0.date > $1.date }) // 降序排序 } }
然后修改View中的按钮点击逻辑和List:
Button { VM.addItem(name: name, int: int) name = "" int = 0 } label: { Text("ADD PERSON") } // List直接用已排序好的数组 List(VM.itemList) { item in Text(item.name) Text("\(item.int)") Text("\(item.date, format: .dateTime.day().month().year())") }
二、修复数值累加问题
单独用@AppStorage存totalNumber容易和条目数据不一致,推荐直接根据条目数组实时计算总和:
方案1:View中实时计算
去掉@AppStorage("TOTAL_NUMBER") var totalNumber = 0,改成计算属性:
struct ContentView: View { // ... 其他代码不变 private var totalNumber: Int { VM.itemList.reduce(0) { $0 + $1.int } } var body: some View { // ... 其他代码不变 Text("\(totalNumber)") .padding() } }
方案2:ViewModel中提供总和属性
如果需要多处使用总和,可在ViewModel中添加计算属性:
class ItemViewModel: ObservableObject { // ... 其他代码不变 var totalNumber: Int { itemList.reduce(0) { $0 + $1.int } } }
然后View中直接调用:
Text("\(VM.totalNumber)") .padding()
修改后的完整View代码
import SwiftUI struct ContentView: View { @State private var name = "" @State private var int = 0 @StateObject var vm = ItemViewModel() private var totalNumber: Int { vm.itemList.reduce(0) { $0 + $1.int } } var body: some View { VStack(spacing: 30) { VStack(alignment: .leading) { HStack { Text("Name:") TextField("Type Here...", text: $name) } HStack { Text("Number:") TextField("Type Here...", value: $int, formatter: NumberFormatter()) } Button { vm.addItem(name: name, int: int) name = "" int = 0 } label: { Text("ADD PERSON") } } .padding() VStack(alignment: .leading) { List(vm.itemList) { item in Text(item.name) Text("\(item.int)") Text("\(item.date, format: .dateTime.day().month().year())") } Text("\(totalNumber)") .padding() } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
内容的提问来源于stack exchange,提问作者madDogAttackToSwiftUI
相关产品推荐
相关产品推荐

