将数据过滤到包含部分的列表中

Filtering data into a list with sections

我有按优先级排序的数据。我正在尝试将该数据显示在列表中:

priority 1
   item with that priority
   item with that priority 
priority 2
   item with that priority
   item with that priority

所以我想用 listsections 来显示优先级,然后在具有该优先级的每个项目下列出。

struct PriorityView: View {
    
    @EnvironmentObject private var taskdata: DataModel
    @State var byPriotity: Int
    
    var body: some View {
        List {
            VStack(alignment: .leading, spacing: 12) {
                // priority 3 is display all items in the list
                if byPriotity == 3 {
                    ForEach(taskdata.filteredTasks(byPriority: byPriotity), id: \.id) { task in
                        Section(header: Text(getTitle(byPriority: task.priority)).font(.headline)) {
                            Text(task.title)
                        }
                    }
                }
            }
        }
    }
}

这只是创建一个列表,其中的一个部分显示优先级,然后是项目的标题。这不是我想要输出的。 我开始阅读有关 section 的文章,一些文章 (this, this, and ) 提供了一些很好的建议,但我一直在试图弄清楚如何迭代并找到具有特定优先级的所有项目,创建一个 header,然后列出所有这些项目的标题。也许我应该添加第二个 forEach 过滤具有特定优先级的所有项目?还是将初始过滤保存在一个变量中,然后对其进行过滤?我不知道。

我试过

            ForEach(taskdata.filteredTasks(byPriority: 3)) { task in
                Section(header: Text(getTitle(byPriority: task.priority)).font(.headline)) {
                    ForEach(task.priority.filter {[=12=].contains(byPriotity) || byPriotity.isEmpty}) {
                        Text(task.title)
                    }
                }
            }

但我不能那样过滤它们。 Int 没有 filter

然后我尝试了

ForEach(task.priority.isEqual(to: byPriotity))

再一次,int 没有 isEqual 的问题。我要在这里兜圈子...

这是我正在尝试的代码:

struct Task: Codable, Hashable, Identifiable {
    var id = UUID()
    var title: String
    var priority = 2
}

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), // comment all this out
              let result = String(data: data, encoding: .utf8)
        else {
            return "[]"
        }
        return result
    }
}

final class DataModel: ObservableObject {
    @AppStorage("tasksp") public var tasks: [Task] = []
    
    init() {
        self.tasks = self.tasks.sorted(by: {
            [=13=].priority < .priority
        })
    }
    
    func sortList() {
        self.tasks = self.tasks.sorted(by: {
            [=13=].priority < .priority
        })
    }
    
    func filteredTasks(byPriority priority: Int) -> [Task] {
        tasks.filter { priority == 3 || [=13=].priority == priority }
    }
    
}

struct PriorityView: View {
    
    @EnvironmentObject private var taskdata: DataModel
    @State var byPriotity: Int
    
    var body: some View {
        List {
            VStack(alignment: .leading, spacing: 12) {
                // priority 3 is display all items in the list
                // I'm going to add another view so the user can 
                // can select what to display eventually
                if byPriotity == 3 {
                    ForEach(taskdata.filteredTasks(byPriority: byPriotity), id: \.id) { task in
                        Section(header: Text(getTitle(byPriority: task.priority)).font(.headline)) {
                            Text(task.title)
                        }
                    }
                }
            }
        }
    }

    func getTitle(byPriority:Int) -> String {
        switch byPriority {
        case 0: return "Important"
        case 1: return "Tomorrow"
        default: return "Someday"
        }
    }
}

编辑:

尝试使用 vadian 建议的代码。我真的不知道我在做什么,但是试过了,结果是这样的:

将结构名称更改为:

struct SectionOrdered : Identifiable {
    var id: String { priority }
    
    let priority: String
    let tasks: [Task]
}

我试过的代码是:

List {
    VStack(alignment: .leading, spacing: 12) {
        if byPriority == 3 {
            ForEach(taskdata.sections) { task in
                Section(header: task.priority) {
                    Text(task.title)
                }
            }
        }
    }
}

vadian 建议过滤 class 中的所有内容,但我不知道如何使用它。我试过了,但我把这里弄得更糟了。

我想知道这是否可能。

要将您的数据分组,首先创建一个结构 Section

struct Section : Identifiable {
    var id : String { priority }
    
    let priority : String
    let tasks : [Task]
}

并在视图模型中 发布 部分并将它们分组,例如在 init 方法中。

final class DataModel: ObservableObject {
    @AppStorage("tasksp") public var tasks: [Task] = []
    
    @Published var sections = [Section]()
    
    init() {
        let grouped = Dictionary(grouping: tasks, by: \.priority)
        let sortedKeys = grouped.keys.sorted()
        self.sections = sortedKeys.map{Section(priority: getTitle(byPriority: [=11=]), tasks: grouped[[=11=]]!)}
    }

    func getTitle(byPriority: Int) -> String {
        switch byPriority {
            case 0: return "Important"
            case 1: return "Tomorrow"
            default: return "Someday"
        }
    }
....

好处是将所有不必要的数据处理都排除在视图之外。

在视图中显示数据

ForEach(taskdata.sections) { section in
    Section {
        ForEach(section.tasks) { task in
            Text(task.title)
        }
    } header: {
       Text(section.priority)
          .font(.headline)
    }
}

如果您想将过滤后的数据分组到多个部分,请先过滤然后再分组。