如果与上一个条目相同,则不显示日期
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...
【问题讨论】:
不相关,但请注意ContentView
和showRow
中的pantry
对象是两个不同的实例。您应该只创建一次 ObservableObject
并将其传递到层次结构中。
感谢您指出我的班级 Pantry 有不止一个实例。看起来我的示例代码已经消失了!我是一名长期从事 C 和汇编工作的固件工程师,我们不必担心类和实例。我的实际代码确实通过层次结构向下传递,并且条目存储在 coredata 中。这是获取:@FetchRequest(实体:CurrTrans.entity(),sortDescriptors:[NSSortDescriptor(keypath:\CurrTrans.entryDT,升序:true)] var currTrans:FetchedResults你不能在你的 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 日期类型来支持以上是关于如果与上一个条目相同,则不显示日期的主要内容,如果未能解决你的问题,请参考以下文章
Hive 查询打印多个条目的 collect_set 值,如果条目是一次则不打印