[英]SwiftUI - Sorting a structured array and show date item into a Section Header
I'm trying to show some section Header which is based on data in my structured array.我试图展示一些 Header 部分,它基于我的结构化数组中的数据。
When I add a value in my array from my app I ask to enter a date.当我从我的应用程序中向我的数组中添加一个值时,我要求输入一个日期。 This one is save as a String cause I don't know how to save it differently from a Textfield..
这个保存为字符串,因为我不知道如何以不同于文本字段的方式保存它。
So in my array I've got an enter "date" formatting like: DD/MM/YYYY
所以在我的数组中,我有一个输入“日期”格式,如:
DD/MM/YYYY
Now I need to have a view where I list all the array Items sorting by date, where the most recent date is show on the top of the screen and more the user scroll down more the date is far in a past.现在我需要一个视图,在其中列出按日期排序的所有数组项目,其中最近的日期显示在屏幕顶部,用户向下滚动的次数越多,日期就已经过去了。
So my structured array is defined like that:所以我的结构化数组是这样定义的:
struct Flight: Identifiable{
let id = UUID().uuidString
let date: String
let depPlace: String
let arrPlace: String
init (date: String, depPlace: String, arrPlace: String){
self.date = date
self.depPlace = depPlace
self.arrPlace = arrPlace
}
init(config: NewFlightConfig){
self.date = config.date
self.depPlace = config.depPlace
self.arrPlace = config.arrPlace
}
}
and NewFlightConfig
:和
NewFlightConfig
:
struct NewFlightConfig{
var date: String = ""
var depPlace: String = ""
var arrPlace: String = ""
}
The TextField
where I ask for the date:我要求日期的
TextField
:
TextField("DD/MM/YYYY", text: $flightConfig.date)
.padding()
.background(.white)
.cornerRadius(20.0)
.keyboardType(.decimalPad)
.onReceive(Just(flightConfig.date)) { inputValue in
if inputValue.count > 10 {
self.flightConfig.date.removeLast()
}else if inputValue.count == 2{
self.flightConfig.date.append("/")
}else if inputValue.count == 5{
self.flightConfig.date.append("/")
}
}
Finally my Homepage with my list which is defined as follow:最后我的主页和我的列表定义如下:
ScrollView{
VStack {
ForEach(flightLibrary.testFlight) {date in
Section(header: Text(date.date).font(.title2).fontWeight(.semibold)) {
ForEach(flightLibrary.testFlight) {flight in
ZStack {
RoundedRectangle(cornerRadius: 16, style: .continuous)
.fill(Color.white)
.shadow(color: Color(Color.RGBColorSpace.sRGB, white: 0, opacity: 0.2), radius: 4)
LogbookCellView(flight: flight)
}
}
}
}
}.padding(.horizontal, 16)
}
Where I've trying to deal with Dictionary
to fill the SectionHeader Text but seems to didn't work...我试图处理
Dictionary
以填充 SectionHeader 文本但似乎没有用的地方......
var entryCollatedByDate: [String : [Flight]] {
Dictionary(grouping: flightLibrary, by: { $0.date })
}
I'm not very familiar with how to sorted data and grouped data into arrays.我不太熟悉如何将数据排序和分组数据放入 arrays。
My final objectif is to have something like that:我的最终目标是拥有类似的东西:
Section Header 1 -> 15/09/2022
Array Items 1 -> last items with same Header date
Array Items 2 -> last items with same Header date
Section Header 2 -> 14/09/2022
Array Items 3 -> last items with same Header date
Array Items 4 -> last items with same Header date
Array Items 5 -> last items with same Header date
[...]
Section Header N -> DD/MM/YYYY
Array Items N -> last items with same Header date
Hope to be clear about my problem希望能清楚我的问题
Thanks for your help谢谢你的帮助
You could try this approach, where a function func asDate(...)
is used to transform your String
date to a Date
on the fly.您可以尝试这种方法,其中使用 function
func asDate(...)
将您的String
日期转换为运行中的Date
。 Then using Set
and map
, to get unique dates for the sections.然后使用
Set
和map
来获取这些部分的唯一日期。 These unique dates are sorted using the func asDate(...)
.这些唯一日期使用
func asDate(...)
进行排序。
struct ContentView: View {
@State var flightLibrary = [Flight(date: "14/09/2022", depPlace: "depPlace-1", arrPlace: "arrPlace-1"),
Flight(date: "15/09/2022", depPlace: "depPlace-2", arrPlace: "arrPlace-2"),
Flight(date: "12/09/2022", depPlace: "depPlace-3", arrPlace: "arrPlace-3"),
Flight(date: "14/09/2022", depPlace: "depPlace-1.2", arrPlace: "arrPlace-1.2")]
func asDate(_ str: String) -> Date {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy"
return dateFormatter.date(from: str) ?? Date()
}
@State var uniqueDates = [String]()
var body: some View {
ScrollView{
VStack {
ForEach(uniqueDates, id: \.self) { date in
Section(header: Text(date).font(.title2).fontWeight(.semibold)) {
ForEach(flightLibrary.filter({$0.date == date})) { flight in
ZStack {
RoundedRectangle(cornerRadius: 16, style: .continuous)
.fill(Color.white)
.shadow(color: Color(Color.RGBColorSpace.sRGB, white: 0, opacity: 0.2), radius: 4)
Text(flight.arrPlace)
}
}
}
}
}.padding(.horizontal, 16)
}
.onAppear {
// unique and sorted dates
uniqueDates = Array(Set(flightLibrary.map{$0.date})).sorted(by: {asDate($0) > asDate($1)})
}
}
}
An alternative approach is to change the String
date in Flight
to type Date
.另一种方法是将
Flight
中的String
日期更改为Date
类型。 Then using Set
, map
and sorted
, to get unique and sorted dates for the sections.然后使用
Set
、 map
和sorted
,获得各部分的唯一和排序日期。
struct ContentView: View {
@State var flightLibrary = [Flight]()
@State var uniqueDates = [Date]()
let frmt = DateFormatter()
var body: some View {
ScrollView{
VStack {
ForEach(uniqueDates, id: \.self) { date in
Section(header: Text(frmt.string(from: date)).font(.title2).fontWeight(.semibold)) {
ForEach(flightLibrary.filter({Calendar.current.isDate($0.date, inSameDayAs: date)})) { flight in
ZStack {
RoundedRectangle(cornerRadius: 16, style: .continuous)
.fill(Color.white)
.shadow(color: Color(Color.RGBColorSpace.sRGB, white: 0, opacity: 0.2), radius: 4)
Text(flight.arrPlace)
}
}
}
}
}.padding(.horizontal, 16)
}
.onAppear {
frmt.dateFormat = "dd/MM/yyyy"
frmt.timeZone = TimeZone(identifier: "UTC")
// for testing only
flightLibrary = [Flight(date: frmt.date(from: "14/09/2022")!, depPlace: "depPlace-1", arrPlace: "arrPlace-1"),
Flight(date: frmt.date(from: "15/09/2022")!, depPlace: "depPlace-2", arrPlace: "arrPlace-2"),
Flight(date: frmt.date(from: "12/09/2022")!, depPlace: "depPlace-3", arrPlace: "arrPlace-3"),
Flight(date: frmt.date(from: "14/09/2022")!, depPlace: "depPlace-1.2", arrPlace: "arrPlace-1.2")]
// unique and sorted dates
uniqueDates = Array(Set(flightLibrary.map{$0.date})).sorted(by: {$0 > $1})
}
}
}
struct Flight: Identifiable, Hashable {
let id = UUID().uuidString
let date: Date // <-- here
let depPlace: String
let arrPlace: String
init (date: Date, depPlace: String, arrPlace: String){ // <-- here
self.date = date
self.depPlace = depPlace
self.arrPlace = arrPlace
}
init(config: NewFlightConfig) {
self.date = config.date
self.depPlace = config.depPlace
self.arrPlace = config.arrPlace
}
}
struct NewFlightConfig {
var date: Date = Date() // <-- here
var depPlace: String = ""
var arrPlace: String = ""
}
EDIT-1: here is another approach that uses a class FlightModel: ObservableObject
to hold your data and update the UI whenever flights
is changed. EDIT-1:这是另一种使用
class FlightModel: ObservableObject
来保存数据并在flights
更改时更新 UI 的方法。 It also has a convenience computed property for the uniqueDates
.它还为
uniqueDates
提供了一个方便的计算属性。 So in your addView
, pass the flightModel
to it (eg @EnvironmentObject) and add new Flight
to the flightModel
.因此,在您的
addView
中,将flightModel
传递给它(例如@EnvironmentObject)并将新的Flight
添加到flightModel
。
class FlightModel: ObservableObject {
@Published var flights = [Flight]()
var uniqueDates: [Date] {
let arr = flights.compactMap{frmt.date(from: frmt.string(from: $0.date))}
return Array(Set(arr.map{$0})).sorted(by: {$0 > $1})
}
let frmt = DateFormatter()
init() {
frmt.dateFormat = "dd/MM/yyyy"
frmt.timeZone = TimeZone(identifier: "UTC")
getData()
}
func getData() {
// for testing only
flights = [
Flight(date: Date(), depPlace: "LFLI", arrPlace: "LFLP"),
Flight(date: Date(), depPlace: "LFLP", arrPlace: "LFLB"),
Flight(date: Date(), depPlace: "LFLB", arrPlace: "LFLG"),
Flight(date: frmt.date(from: "14/09/2022")!, depPlace: "depPlace-1", arrPlace: "arrPlace-1"),
Flight(date: frmt.date(from: "15/09/2022")!, depPlace: "depPlace-2", arrPlace: "arrPlace-2"),
Flight(date: frmt.date(from: "12/09/2022")!, depPlace: "depPlace-3", arrPlace: "arrPlace-3"),
Flight(date: frmt.date(from: "14/09/2022")!, depPlace: "depPlace-1.2", arrPlace: "arrPlace-1.2")
]
}
}
struct ContentView: View {
@StateObject var flightModel = FlightModel()
var body: some View {
ScrollView {
VStack {
ForEach(flightModel.uniqueDates, id: \.self) { date in
Section(header: Text(flightModel.frmt.string(from: date)).font(.title2).fontWeight(.semibold)) {
ForEach(flightModel.flights.filter({Calendar.current.isDate($0.date, inSameDayAs: date)})) { flight in
ZStack {
RoundedRectangle(cornerRadius: 16, style: .continuous)
.fill(Color.white)
.shadow(color: Color(Color.RGBColorSpace.sRGB, white: 0, opacity: 0.2), radius: 4)
Text(flight.arrPlace)
}
}
}
}
}.padding(.horizontal, 16)
}
}
}
In response to @workingdog support Ukraine in the comments just before.作为对@workingdog 在之前评论中支持乌克兰的回应。
I have no errors in my code but with this array:我的代码中没有错误,但是使用了这个数组:
class FlightLibrary: ObservableObject{
@Published var testFlight = [
Flight(date: Date(), depPlace: "LFLI", arrPlace: "LFLP"),
Flight(date: Date(), depPlace: "LFLP", arrPlace: "LFLB"),
Flight(date: Date(), depPlace: "LFLB", arrPlace: "LFLG")
]
}
This return something like:这返回类似于:
Section Header 1 : Today Date
Item 1 :testFlight[0]
Item 2 :testFlight[0]
Item 3 :testFlight[0]
Section Header 2 : Today Date
Item 1 :testFlight[0]
Item 2 :testFlight[0]
Item 3 :testFlight[0]
When I append a value into testFlight, via a page in the app where user could fill some textFields to set date, dePlace and arrPlace, then dismiss my page the scrollview is not updated correctly.当我通过应用程序中的页面将值 append append 输入 testFlight 时,用户可以在其中填写一些 textFields 以设置日期、dePlace 和 arrPlace,然后关闭我的页面,滚动视图未正确更新。 I've my new item but I have no Section Header...
我有我的新项目,但我没有 Header 部分...
My add button code in the other view:我在另一个视图中添加按钮代码:
@Environment(\.dismiss) private var dismiss
@ObservedObject var flightLibrary: FlightLibrary
@State var flightConfig = NewFlightConfig()
.navigationBarItems(trailing: Button(action: {
let newFlight = Flight(config: flightConfig)
flightLibrary.testFlight.append(newFlight)
dismiss()
}, label: {
Text("Save")
}))
To update the scrollview I go in an other page and back (to reload the .onAppear
) to the page where I have my scrollview and now it works with all section header but !要更新滚动视图,我 go 在另一个页面中并返回(重新加载
.onAppear
)到我有滚动视图的页面,现在它适用于所有部分 header 但是!
If there is only one date section header is correct but if there is two or more item with the same date it create a new section header for each item but it add all the item with the same date in each section...如果只有一个日期部分 header 是正确的,但如果有两个或多个具有相同日期的项目,它会为每个项目创建一个新的部分 header 但它会在每个部分中添加所有具有相同日期的项目...
Exemple:示例:
Item1 = Date 1
Item2 = Date 1
Item3 = Date 2
Item4 = Date 1
result of :
Section Header 1 : Date1
Item 1
Item 2
Item 4
Section Header 2 : Date1
Item 1
Item 2
Item 4
Section Header 3 : Date1
Item 1
Item 2
Item 4
Section Header 4 : Date2
Item 3
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.