具有自定义数据结构的 SwiftUI 列表,每个循环都嵌套 [关闭]

Posted

技术标签:

【中文标题】具有自定义数据结构的 SwiftUI 列表,每个循环都嵌套 [关闭]【英文标题】:SwiftUI List with custom data structure with nested for each loops [closed] 【发布时间】:2021-10-31 02:07:16 【问题描述】:

我已经能够在我的 swiftui 应用程序中成功实现无限滚动,但它的行为不正确,它与它迭代的列表有关。例如,我从 graphql 端点获取产品订单列表并将其存储为

@Published var groupedPurchaseOrders = [[PurchaseOrderFragment]]()

它嵌套在数组中的原因是因为我将这些数据处理成按订单日期分隔的组。

func groupPurchaseOrders() 
        
    let dateGroups = Dictionary(grouping: purchaseOrders)  (element) -> Date in
        return element.orderForDate.reduceToMonthDayYear()
    
      
    let sortedKeys = dateGroups.keys.sorted  (Date1, Date2) -> Bool in
        Date1 > Date2
    
        
    sortedKeys.forEach  key in
        let values = dateGroups[key]
        groupedPurchaseOrders.append(values)
    

这样我可以创建一个列表,其中包含带有日期的标题(我从列表中的第一项派生)然后是采购订单。

ForEach(posModel.groupedPurchaseOrders, id: \.self)  group in         
    Section(header: <Derived date from first item>) 
        ForEach(group, id: \.self)  order in
            NavigationLink(destination: PurchaseOrderDetailView(purchaseOrder: order)) 
                PurchaseOrderListItemOnlyView(purchaseOrder: order)
                    .padding(.vertical, 4)
                    .onAppear 
                         let purchaseOrders = posModel.groupedPurchaseOrders.flatMap  $0 
                         if purchaseOrders.last == order 
                              self.loadOrders()
                         
            
        
    


Date
    Order
    Order
Date
    Order
    ...

问题

列表可以正常加载前 20 个项目,但是当它到达列表底部并加载更多(在本例中为 5 个项目)时,它会添加 5,列表具有原始的 20 加 25。但是当我创建了一个没有分组的普通列表,它按预期工作,只是将 5 附加到末尾。

作为 ios dev 的初学者,我不确定是否可以创建类似的数据结构 [日期,[PurchaseOrderFragment]] 与 [PurchaseOrderFragment] 相对。我已经尝试过了,但似乎无法正确地获得语法和实现......关于如何通过分组实现列表的任何想法?它在哪里正确附加记录?

编辑

我试过了

struct GroupedOrder: Hashable 
    var date: Date
    var orders: [PurchaseOrderFragment]
    
    public func hash(into hasher: inout Hasher) 
        hasher.combine(date)
        hasher.combine(orders)
    

当它再加载 5 个时,它仍然会复制列表

【问题讨论】:

可以创建一个类型为 (Date, [PurchaseOrderFragment]) 的元组,但似乎只创建一个包装器会更有意义:struct PurchaseWrapper var date: Date; var orders: [PurchaseOrderFragment] 然后迭代这些. @jnpdx 我正是这样做的,它只是复制了前 20 个,并在负载上增加了 5 个。您认为这可能是 List 组件的限制吗? 不——它与您的 groupPurchaseOrders 函数有关。你所做的就是append。我认为您在这里没有足够的信息/代码来诊断逻辑应该是什么,但是您需要确定是否需要替换整个数组,附加到它等等。 列表中有45个元素,但打印出模型中列表的计数,有25个,似乎在列表中 你能加入minimal reproducible example吗?否则无法帮助调试细节。 【参考方案1】:

我根据您的代码编写了一个游乐场。它缺少的是您的代码未显示的“再加载 5 个”的概念。但是分组日期只是由与原始代码相同的Dictionary(grouping: purchaseOrders) $0.date 组成。从理论上讲,您可以使用任何采购订单列表。 `

import UIKit
import SwiftUI
import PlaygroundSupport

let mockPurchaseOrders = """
[
     "date" : "1/1/2020", "title": "The First Purchase Order" ,
     "date" : "1/1/2020", "title": "The Second Purchase Order" ,
     "date" : "1/1/2020", "title": "The Third Purchase Order" ,
     "date" : "3/2/2020", "title": "The Fourth Purchase Order" ,
     "date" : "3/2/2020", "title": "The Fifth Purchase Order" ,
     "date" : "5/1/2020", "title": "The Sixth Purchase Order" ,
     "date" : "7/1/2020", "title": "The Seventh Purchase Order" ,
     "date" : "7/1/2020", "title": "The Eighth Purchase Order" ,
     "date" : "7/1/2020", "title": "The Ninth Purchase Order" 
]
"""

struct PurchaseOrder : Decodable, Hashable 
    static var dateFormatter : DateFormatter = 
        let formatter = DateFormatter()
        formatter.dateStyle = .short
        formatter.timeStyle = .none
        return formatter;
    ()

    let title : String
    let date : Date

    init(from decoder: Decoder) throws 
        let container = try decoder.container(keyedBy: Self.CodingKeys)
        title = try container.decode(String.self, forKey: CodingKeys.title)

        let dateString = try container.decode(String.self, forKey: .date)
        date = PurchaseOrder.dateFormatter.date(from: dateString)!

    

    enum CodingKeys: String, CodingKey 
        case title
        case date
    


typealias GroupedDates = [Date : [PurchaseOrder]]

struct NestedListView : View 
    let groupedOrders : GroupedDates
    var orderDates : [Date]  Array<Date>(groupedOrders.keys).sorted() 
    static var dateFormatter : DateFormatter = 
        let formatter = DateFormatter()
        formatter.dateStyle = .short
        formatter.timeStyle = .none
        return formatter;
    ()

    init(orders : GroupedDates) 
        groupedOrders = orders
    

    var body : some View 
        ForEach(orderDates, id: \.self)  date in
            Section(header: Text(NestedListView.dateFormatter.string(from: date))) 
                ForEach(groupedOrders[date]!, id: \.self)  order in
                    NavigationLink(destination: Text("Hello!")) 
                        Text(order.title)
                    
                
            
        
        .frame(minWidth: 320, idealWidth: 320, maxWidth: 320, minHeight: 480)
    



let jsonDecoder = JSONDecoder()
let purchaseOrders = try jsonDecoder.decode([PurchaseOrder].self, from:  mockPurchaseOrders.data(using: .utf8)!)
let groupedOrders = Dictionary(grouping: purchaseOrders)  $0.date 

let hostingController = UIHostingController(rootView: NestedListView(orders: groupedOrders))
PlaygroundSupport.PlaygroundPage.current.liveView = hostingController

【讨论】:

感谢您发布此内容,非常感谢,但我发现在集合分组功能中我再次获得了整个列表,因此重复,但我从您的示例中学到了很多关于如何重新实现的知识我会检查它作为答案 哎呀!我想我在发布我的解决方案之前大约一个小时错过了你的 cmets!感谢您的客气话,我很高兴这可以帮助您学习。

以上是关于具有自定义数据结构的 SwiftUI 列表,每个循环都嵌套 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI - 多选列表(不是自定义列表)

SwiftUI 列表中的自定义按钮

SwiftUI:禁用具有自定义 ButtonStyle 的 NavigationLink 的最佳方法

SwiftUI:自定义按钮无法识别具有清晰背景和 buttonStyle 的触摸

SwiftUI 如何在其中创建带有自定义 UIViews 的列表

在 SwiftUI 中创建一个包含自定义类数组的列表