如果与上一个条目相同,则不显示日期

Posted

技术标签:

【中文标题】如果与上一个条目相同,则不显示日期【英文标题】:Don't Display Date if Same as Previous Entry 【发布时间】:2021-05-06 20:04:24 【问题描述】:

我有一个条目列表,每个条目都附有日期。我只想在日期发生变化时才显示日期。我首先在 ios 14.4 中开发了这个软件,导致视图不可变错误。这是因为我正在存储和更改输入日期的副本。

现在在 iOS 14.5 版本中,我看不到不可变错误。但是我的软件还是不行。如果您运行代码并在控制台中查看,您会注意到 Xcode 两次检查了我的六个条目:第一次始终为真(显示日期),第二次始终为假(不显示日期)。为什么?

在我的实际代码中,我在此示例代码中使用 Date 类型的日期而不是字符串。在我的实际代码中,操作挂起,因为它通过我的函数 checkDate 无休止地循环(比条目数多很多倍)。 Date 类型的日期是否包括导致比较失败的时间?

如果日期与上一个条目相同,是否有更好的方法来防止显示日期?

struct KitchenItem:  Codable, Identifiable 
    var id = UUID()
    var item: String
    var itemDate: String
    var itemCost: Double


class Pantry: ObservableObject 
    
    @Published var oldDate: String = ""
    @Published var kitchenItem: [KitchenItem]
    
    init() 
        self.kitchenItem = []
        
        let item0 = KitchenItem(item: "String Beans", itemDate: "1/13/2021", itemCost: 4.85)
        self.kitchenItem.append(item0)
        
        let item1 = KitchenItem(item: "Tomatoes", itemDate: "1/22/2021", itemCost: 5.39)
        self.kitchenItem.append(item1)
        
        let item2 = KitchenItem(item: "Bread", itemDate: "1/22/2021", itemCost: 4.35)
        self.kitchenItem.append(item2)
        
        let item3 = KitchenItem(item: "Corn", itemDate: "3/18/2021", itemCost: 2.75)
        self.kitchenItem.append(item3)
        
        let item4 = KitchenItem(item: "Peas", itemDate: "3/18/2021", itemCost: 7.65)
        self.kitchenItem.append(item4)
        
        let item5 = KitchenItem(item: "Ice Cream", itemDate: "4/12/2021", itemCost: 7.95)
        self.kitchenItem.append(item5)
    



struct ContentView: View 
    
    @ObservedObject var pantry: Pantry = Pantry()
    
    var body: some View 
        
        LazyVStack (alignment: .leading) 
            Text("Grandma's Food Pantry")
                .font(.title)
                .fontWeight(.bold)
                .padding(.top, 36)
                .padding(.leading, 36)
                .padding(.bottom, 30)
            
            ForEach(0..<pantry.kitchenItem.count, id: \.self)  item in
                
                VStack (alignment: .leading) 
                    showRow(item: item)
                
            
        
    



struct showRow: View 
    
    @ObservedObject var pantry: Pantry = Pantry()
    
    var item: Int
    
    var body: some View 
        
        // don't show the date if is the same as the previous entry
        let newDate = pantry.kitchenItem[item].itemDate
        if checkDate(newDate: newDate) == true 
            Text("\n\(newDate)")
                .font(.title2)
                .padding(.leading, 10)
        
        
        HStack 
            Text("\(pantry.kitchenItem[item].item)")
                .padding(.leading, 50)
                .frame(width: 150, alignment: .leading)
            
            Text("\(pantry.kitchenItem[item].itemCost, specifier: "$%.2f")")
        
    
    
    func checkDate(newDate: String) -> (Bool) 
        print("    ")
        print("new date = \(newDate)")
       
        if newDate == pantry.oldDate 
            print("false: don't show the date")
            return false
         else 
            pantry.oldDate = newDate
            print("old date = \(pantry.oldDate)")
            print("true: show the date")
            return true
        
    

实际代码:

struct ListView: View 
    
    @EnvironmentObject var categories: Categories
    @EnvironmentObject var userData: UserData
    @Environment(\.managedObjectContext) var viewContext
    
    var money: String = ""
    var xchRate: Double = 0.0
    var cat: Int = 0
    var mny: String = ""
    
    @FetchRequest(
        entity: CurrTrans.entity(),
        sortDescriptors: [NSSortDescriptor(keyPath: \CurrTrans.entryDT, ascending: true)]
    ) var currTrans: FetchedResults<CurrTrans>
    
    var body: some View 
        
        GeometryReader  g in
            ScrollView 
                LazyVStack (alignment: .leading) 
                    
                    TitleView()
                    
                    ForEach(currTrans, id: \.self)  item in
                            showRow(item: item, priorDate: priorDate(forItemIndex: item), g: g)
                    
                    .onDelete(perform: deleteItem)
                
                .font(.body)
            
        
    
    
    private func priorDate(forItemIndex item: Int) -> Date? 
           guard item > 0 else  return nil 
           return currTrans[item - 1].entryDT
       

struct showRow: View 
    
    @EnvironmentObject var userData: UserData
    
    var item: CurrTrans
    var priorDate: Date?
    var g: GeometryProxy
    
    var payType = ["Cash/Debit", "Credit"]
    
    var body: some View 
        
        // don't show the date if is the same as the previous entry
        let gotDate = item.entryDT ?? Date()
        let newDate = gotDate.getFormattedDate()
            Text("\(newDate)")
                .opacity(gotDate == priorDate ? 0 : 1)
                .font(.title2)
                .padding(.leading, 10)
     
          displays entry parameters in HStack...

【问题讨论】:

不相关,但请注意ContentViewshowRow 中的pantry 对象是两个不同的实例。您应该只创建一次 ObservableObject 并将其传递到层次结构中。 感谢您指出我的班级 Pantry 有不止一个实例。看起来我的示例代码已经消失了!我是一名长期从事 C 和汇编工作的固件工程师,我们不必担心类和实例。我的实际代码确实通过层次结构向下传递,并且条目存储在 coredata 中。这是获取:@FetchRequest(实体:CurrTrans.entity(),sortDescriptors:[NSSortDescriptor(keypath:\CurrTrans.entryDT,升序:true)] var currTrans:FetchedResults 【参考方案1】:

你不能在你的 body 方法中改变你的数据,因为这在 SwiftUI 眼中是可憎的。

body 方法内修改oldDate 是错误的。如果您在渲染视图时修改它正在观察的数据,SwiftUI 会感到困惑。此外,SwiftUI 不保证它呈现 LazyVStack(或任何其他容器)的子级的顺序。

如果日期与上一个条目相同,是否有更好的方法来防止显示日期?

是的。将当前条目和之前条目的日期传递到条目视图。

这是您的数据模型和存储,没有繁琐:

struct KitchenItem:  Codable, Identifiable 
    var id = UUID()
    var item: String
    var itemDate: String
    var itemCost: Double


class Pantry: ObservableObject 
    @Published var kitchenItems: [KitchenItem] = [
        .init(item: "String Beans", itemDate: "1/13/2021", itemCost: 4.85),
        .init(item: "Tomatoes", itemDate: "1/22/2021", itemCost: 5.39),
        .init(item: "Bread", itemDate: "1/22/2021", itemCost: 4.35),
        .init(item: "Corn", itemDate: "3/18/2021", itemCost: 2.75),
        .init(item: "Peas", itemDate: "3/18/2021", itemCost: 7.65),
        .init(item: "Ice Cream", itemDate: "4/12/2021", itemCost: 7.95),
    ]

对于每个KitchenItem,如果有前项,您还需要提取前项的日期。我们将使用一个辅助方法priorDate(forItemIndex:) 来做到这一点。此外,如果您要在视图中创建商店,您需要使用StateObject,而不是ObservedObject。因此:

struct ContentView: View 

    @StateObject var pantry: Pantry = Pantry()

    var body: some View 
        ScrollView(.vertical) 
            LazyVStack (alignment: .leading) 
                Text("Grandma's Food Pantry")
                    .font(.title)
                    .fontWeight(.bold)
                    .padding(.top, 36)
                    .padding(.leading, 36)
                    .padding(.bottom, 30)

                ForEach(0 ..< pantry.kitchenItems.count)  i in
                    if i > 0 
                        Divider()
                    
                    KitchenItemRow(item: pantry.kitchenItems[i], priorDate: priorDate(forItemIndex: i))
                
            
        
    

    private func priorDate(forItemIndex i: Int) -> String? 
        guard i > 0 else  return nil 
        return pantry.kitchenItems[i - 1].itemDate
    

这里是KitchenItemRow。如果日期与前一个项目的日期相同,您可以看到它使日期 Text 透明。我将其保留在原位,但使其透明,因此行布局相同:

struct KitchenItemRow: View 
    var item: KitchenItem
    var priorDate: String?

    var body: some View 
        VStack(alignment: .leading) 
            HStack 
                Text(item.item)
                Spacer()
                Text("\(item.itemCost, specifier: "$%.2f")")
            
            Text(item.itemDate)
                .opacity(item.itemDate == priorDate ? 0 : 1)
        
        .padding([.leading, .trailing], 10)
    

这里是TitleView,为了卫生,从ContentView中提取:

struct TitleView: View 
    var body: some View 
        Text("Grandma's Food Pantry")
            .font(.title)
            .fontWeight(.bold)
            .padding(.top, 36)
            .padding(.leading, 36)
            .padding(.bottom, 30)

    

结果:

更新

由于您的“真实代码”使用 onDelete,因此为每个项目提供 ForEach 的 ID 而不是使用索引非常重要。

注意onDelete only works inside List, not inside LazyVStack

所以我们需要将每个项目映射到它的索引,这样我们才能找到之前的项目。这是我的ContentView 的修订版,它使用了onDelete

struct ContentView: View 

    @StateObject var pantry: Pantry = Pantry()

    var body: some View 
        let indexForItem: [UUID: Int] = .init(
            uniqueKeysWithValues: pantry.kitchenItems.indices.map 
                (pantry.kitchenItems[$0].id, $0) )

        List 
            TitleView()

            ForEach(pantry.kitchenItems, id: \.id)  item in
                let i = indexForItem[item.id]!
                KitchenItemRow(item: item, priorDate: priorDate(forItemIndex: i))
            
            .onDelete(perform: deleteItems(at:))
        
    

    private func priorDate(forItemIndex i: Int) -> String? 
        guard i > 0 else  return nil 
        return pantry.kitchenItems[i - 1].itemDate
    

    private func deleteItems(at offsets: IndexSet) 
        pantry.kitchenItems.remove(atOffsets: offsets)
    

在我的测试中,这有效并且允许滑动删除。我相信您可以将其调整为您的“真实”代码。

【讨论】:

嗨 Rob:我在上面包含了我的实际代码。我不确定如何使用返回类型 Date 来实现 priorDate 私有函数?我需要通过我的 coredata 日期类型来支持

以上是关于如果与上一个条目相同,则不显示日期的主要内容,如果未能解决你的问题,请参考以下文章

仅在最大日期而不是在组的其他条目中显示具有特定值的组

如果字典数组由“nan”组成,则不返回任何条目

查询重复日期

Hive 查询打印多个条目的 collect_set 值,如果条目是一次则不打印

如果 'object' 的值为 0,则不显示 'level' 输入

Selenium (Eclipse) - 功能文件 - 如果未定义步骤定义,则不突出显示步骤