我正在尝试重新创建与库存的iOS日历应用程序中的事件列表匹配的视图。我有以下代码生成事件列表,每个事件按日期分为自己的部分:
var body: some View {
NavigationView {
List {
ForEach(userData.occurrences) { occurrence in
Section(header: Text("\(occurrence.start, formatter: Self.dateFormatter)")) {
NavigationLink(
destination: OccurrenceDetail(occurrence: occurrence)
.environmentObject(self.userData)
) {
OccurrenceRow(occurrence: occurrence)
}
}
}
}
.navigationBarTitle(Text("Events"))
}.onAppear(perform: populate)
}
此代码的问题是,如果在同一日期有两个事件,则将它们分为具有相同标题的不同部分,而不是将它们分组到同一部分中。
作为Swift新手,我的本能是做这样的事情:
ForEach(userData.occurrences) { occurrence in
if occurrence.start != self.date {
Section(header: Text("\(occurrence.start, formatter: Self.dateFormatter)")) {
NavigationLink(
destination: OccurrenceDetail(occurrence: occurrence)
.environmentObject(self.userData)
) {
OccurrenceRow(occurrence: occurrence)
}
}
} else {
NavigationLink(
destination: OccurrenceDetail(occurrence: occurrence)
.environmentObject(self.userData)
) {
OccurrenceRow(occurrence: occurrence)
}
}
self.date = occurrence.start
但是在Swift中,这会给我错误“无法推断复杂的闭包返回类型;添加明确的类型以消除歧义”,因为我在ForEach {}中调用了任意代码(self.date =出现率.start),不允许。
实现此目的的正确方法是什么?是否有更动态的方式来执行此操作,还是我需要以某种方式在ForEach {}之外抽象代码?
编辑:出现对象看起来像这样:
struct Occurrence: Hashable, Codable, Identifiable {
var id: Int
var title: String
var description: String
var location: String
var start: Date
var end: String
var cancelled: Bool
var public_occurrence: Bool
var created: String
var last_updated: String
private enum CodingKeys : String, CodingKey {
case id, title, description, location, start, end, cancelled, public_occurrence = "public", created, last_updated
}
}
更新: 以下代码为我提供了一个字典,其中包含以相同日期为键的出现数组:
let myDict = Dictionary( grouping: value ?? [], by: { occurrence -> String in
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .none
return dateFormatter.string(from: occurrence.start)
})
self.userData.latestOccurrences = myDict
但是,如果我尝试在“视图”中使用它,如下所示:
ForEach(self.occurrencesByDate) { occurrenceSameDate in
// Section(header: Text("\(occurrenceSameDate[0].start, formatter: Self.dateFormatter)")) {
ForEach(occurrenceSameDate, id: occurrenceSameDate.id){ occurrence in
NavigationLink(
destination: OccurrenceDetail(occurrence: occurrence)
.environmentObject(self.userData)
) {
OccurrenceRow(occurrence: occurrence)
}
}
// }
}
(在我工作的主要部分中,部分内容已被注释掉)
我收到此错误:无法将类型'_.Element'的值转换为预期的参数类型'Occurrence'
答案 0 :(得分:2)
关于我对您的问题的评论,应在显示数据之前将其分成几部分。
这个想法是有一个对象数组,其中每个对象都包含一个事件数组。因此,我们简化了您的出现对象(对于本示例),并创建了以下内容:
struct Occurrence: Identifiable {
let id = UUID()
let start: Date
let title: String
}
接下来,我们需要一个对象来表示给定日期发生的所有事件。我们将其称为Day
对象,但是名称在此示例中并不太重要。
struct Day: Identifiable {
let id = UUID()
let title: String
let occurrences: [Occurrence]
let date: Date
}
因此,我们要做的是获取Occurrence
对象的数组并将其转换为Day
对象的数组。
我创建了一个简单的结构,该结构执行使此操作发生所需的所有任务。显然,您希望对其进行修改,以使其与您拥有的数据相匹配,但是关键是要拥有一个Day
对象数组,然后可以轻松显示这些对象。我在代码中添加了注释,以便您可以清楚地看到每件事。
struct EventData {
let sections: [Day]
init() {
// create some events
let first = Occurrence(start: EventData.constructDate(day: 5, month: 5, year: 2019), title: "First Event")
let second = Occurrence(start: EventData.constructDate(day: 5, month: 5, year: 2019, hour: 10), title: "Second Event")
let third = Occurrence(start: EventData.constructDate(day: 5, month: 6, year: 2019), title: "Third Event")
// Create an array of the occurrence objects and then sort them
// this makes sure that they are in ascending date order
let events = [third, first, second].sorted { $0.start < $1.start }
// create a DateFormatter
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .none
// We use the Dictionary(grouping:) function so that all the events are
// group together, one downside of this is that the Dictionary keys may
// not be in order that we require, but we can fix that
let grouped = Dictionary(grouping: events) { (occurrence: Occurrence) -> String in
dateFormatter.string(from: occurrence.start)
}
// We now map over the dictionary and create our Day objects
// making sure to sort them on the date of the first object in the occurrences array
// You may want a protection for the date value but it would be
// unlikely that the occurrences array would be empty (but you never know)
// Then we want to sort them so that they are in the correct order
self.sections = grouped.map { day -> Day in
Day(title: day.key, occurrences: day.value, date: day.value[0].start)
}.sorted { $0.date < $1.date }
}
/// This is a helper function to quickly create dates so that this code will work. You probably don't need this in your code.
static func constructDate(day: Int, month: Int, year: Int, hour: Int = 0, minute: Int = 0) -> Date {
var dateComponents = DateComponents()
dateComponents.year = year
dateComponents.month = month
dateComponents.day = day
dateComponents.timeZone = TimeZone(abbreviation: "GMT")
dateComponents.hour = hour
dateComponents.minute = minute
// Create date from components
let userCalendar = Calendar.current // user calendar
let someDateTime = userCalendar.date(from: dateComponents)
return someDateTime!
}
}
然后,这允许ContentView
简单地是两个嵌套的ForEach
。
struct ContentView: View {
// this mocks your data
let events = EventData()
var body: some View {
NavigationView {
List {
ForEach(events.sections) { section in
Section(header: Text(section.title)) {
ForEach(section.occurrences) { occurrence in
NavigationLink(destination: OccurrenceDetail(occurrence: occurrence)) {
OccurrenceRow(occurrence: occurrence)
}
}
}
}
}.navigationBarTitle(Text("Events"))
}
}
}
// These are sample views so that the code will work
struct OccurrenceDetail: View {
let occurrence: Occurrence
var body: some View {
Text(occurrence.title)
}
}
struct OccurrenceRow: View {
let occurrence: Occurrence
var body: some View {
Text(occurrence.title)
}
}
这是最终结果。
答案 1 :(得分:1)
这实际上是两个问题。
在数据部分,请将userData.occurrences
从[出现次数]升级到[[
发生
]](我在这里叫latestOccurrences
)
var self.userData.latestOccurrences = Dictionary(grouping: userData.occurrences) { (occurrence: Occurrence) -> String in
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .none
return dateFormatter.string(from: occurrence.start)
}.values
在swiftUI中,您只需重组最新数据:
NavigationView {
List {
ForEach(userData.latestOccurrences, id:\.self) { occurrenceSameDate in
Section(header: Text("\(occurrenceSameDate[0].start, formatter: DateFormatter.init())")) {
ForEach(occurrenceSameDate){ occurrence in
NavigationLink(
destination: OccurrenceDetail(occurrence: occurrence)
.environmentObject(self.userData)
) {
OccurrenceRow(occurrence: occurrence)
}
}
}
}
}
.navigationBarTitle(Text("Events"))
}.onAppear(perform: populate)