You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 09:15:35