在 SwiftUI 中分组列表数据以分段显示

Posted

技术标签:

【中文标题】在 SwiftUI 中分组列表数据以分段显示【英文标题】:Group list data in SwiftUI for display in sections 【发布时间】:2020-06-04 08:03:35 【问题描述】:

我正在尝试获取一组项目(结构)并使用 SwiftUI 在分组表视图中显示它们。

我的(简化的)模型如下所示:

struct CheckIn: Identifiable 
  ...
  let id = UUID()
  let date = Date().atMidnight // removes the time component
  var completed: Bool
  ...


class Store: ObservableObject 
  @Published var checkIns = [...] 
    didSet  persist() 
  

在列表中显示签到之前,我想按日期对它们进行分组。所以我有另一个模型:

struct DailyCheckIns 
  let date: Date
  let checkIns: [CheckIn]


// and a function to group the check-ins array:
func groupByDate(_ checkIns: [CheckIn]) -> [DailyCheckIns] ...

视图是我遇到问题的地方。下面的版本有效,但数据没有明显分组。 “工作”是指数据被传递到CheckInView,它可以更新其签入,然后正确反映在商店和 UI 中。

struct ContentView: View 
  @EnvironmentObject var store: Store

  var body: some View 
    NavigationView 
      List 
        ForEach(store.checkIns.indices)  idx in
          CheckInView(checkIn: self.$store.checkIns[idx]) // checkIn is a @Binding
        
      
      .navigationBarTitle("Check Ins")
    
  

下一个版本是我对数据进行分组的尝试。使用这种方法,我必须将CheckInViewcheckIn 属性从@Binding 更改为@State。分组工作并显示数据,但是当签入完成被切换时,模型会更新,但 UI 不会。

struct ContentView: View 
  @EnvironmentObject var store: Store

  var body: some View 
    NavigationView 
      List 
        ForEach(groupByDate(store.checkIns), id: \.date)  daily in
          Section(header: Text(dateFormatter.string(from: daily.date))) 
            ForEach(daily.checkIns, id: \.id)  checkIn in
              CheckInView(checkIn: checkIn) // I can't use a binding here, so in this version I need to make checkIn a @State.
            
          
        
      
      .navigationBarTitle("Check Ins")
    
  

目前我没有CheckInView直接修改签到。相反,它将更新发布到商店,商店更新模型:

struct CheckInView: View 
  @Binding var checkIn: CheckIn
  @EnvironmentObject var store: Store

  var body: some View 
    HStack 
      Button(action: 
        self.store.update(checkIn: self.checkIn, with: true)

      ) 
        Image(systemName: "...")
          .font(.largeTitle)
          .foregroundColor(checkIn.completed ? .gray : .red)
      
      .buttonStyle(BorderlessButtonStyle())
...

所以问题是:如何保持列表分组并保持绑定在视图层次结构中一直有效?

【问题讨论】:

您是否尝试将修饰符 .listStyle(GroupedListStyle()) 添加到您的列表中? 抱歉,我在深夜打字,弄乱了我的术语。我不是指表格视图的样式,只是数据应该按日期分组。 【参考方案1】:

我已经想出了如何做到这一点。我不知道这是否是最优雅的解决方案。它在我看来确实不优雅,但我想不出另一种方法来做到这一点。

在我发布的代码中,DailyCheckIns 是用于对签到进行分组的中间模型。函数groupByDate 接受CheckIns 的数组并将它们转换为DailyCheckIns 进行显示。问题是DailyCheckIn 本质上保存了存储中数据的副本,所以我不能真正使用它来创建从该数据到存储的绑定。

我发现的方法是使用DailyCheckIns 来创建部分和每个部分中的行数,但是在创建需要绑定到商店的视图时,我直接使用商店的数据.为此,我必须更改 DailyCheckIns(和 groupByDate)以跟踪商店属性中每个 CheckIn 的索引:

typealias CheckInWithIndex = (Int, CheckIn)

struct DailyCheckIns 
  let date: Date
  var checkIns: [CheckInWithIndex]

  func appending(_ ci: CheckInWithIndex) -> DailyCheckIns 
    DailyCheckIns(date: date, checkIns: checkIns + [ci])
  


private func groupByDate(_ checkIns: [CheckIn]) -> [DailyCheckIn]  ... 

struct ContentView: View 
  @EnvironmentObject var store: Store

  var body: some View 
    NavigationView 
      List 
        ForEach(groupByDate(store.checkIns), id: \.date)  daily in
          Section(header: Text(dateFormatter.string(from: daily.date))) 
            ForEach(daily.checkIns, id: \.id)  checkInWithIndex in
              CheckInView(checkIn: self.$store.checkIns[checkInWithIndex.0])
            
          
        
      
      .navigationBarTitle("Check Ins")
    
  

希望这对某人有所帮助。而且,我希望有人对这种情况有更好的解决方案。

【讨论】:

以上是关于在 SwiftUI 中分组列表数据以分段显示的主要内容,如果未能解决你的问题,请参考以下文章

按月/年分组 FechedResults 以显示带有部分的 swiftui 列表

使用 NSFetchedResultsController 在分段表视图中排序和显示数据

SwiftUI .swipeactions 和核心数据

SwiftUI 工作表显示数据错误的工作表

SwiftUI:.sheet() 在关闭当前工作表时不会使用预期数据转到上一个视图

SwiftUI 列表,其中包含从核心数据中获取的部分