我有一个应用程序,其中包含允许用户上传一个餐点到firebase供以后查看,我想在TableView中以降序(按日期)显示这些餐点,并按它们被记录的日期和相应的部分进行排序。
餐点对象的定义如下
class Meal: NSObject, Codable {
var id: String?
var foodname: String?
var quantity: Float!
var brandName: String?
var quantityType: String?
var calories: Float!
var date: Date?
}
而我的TableView目前看起来是这样的
class MealsTableViewController: UITableViewController {
var databaseController: DatabaseProtocol?
var sections = Dictionary<String, Array<Meal>>()
var sortedSections = [String]()
var listOfMeals = [Meal]() {
didSet {
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
let appDelegate = UIApplication.shared.delegate as! AppDelegate
databaseController = appDelegate.databaseController
let accountsRef = databaseController?.getAccountsRef()
accountsRef?.document(SingletonAccount.shared.userEmail).collection("meals")
.getDocuments { (querySnapshot, error) in
if error != nil {
print("Error retrieving all meals!")
}else{
do{
for document in querySnapshot!.documents
{
let currentMeal = Meal()
let data = try! document.data()
currentMeal.id = data["id"] as? String ?? ""
currentMeal.brandName = data["brandName"] as? String ?? ""
currentMeal.calories = data["calories"] as? Float ?? 0.0
let parsingDate = data["date"] as! Timestamp
currentMeal.date = parsingDate.dateValue()
currentMeal.foodname = data["foodname"] as? String
currentMeal.quantity = data["quantity"] as? Float
currentMeal.quantityType = data["quantityType"] as? String
print(currentMeal.date)
self.listOfMeals.append(currentMeal)
}
}catch{
print(error)
}
}
}
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return listOfMeals.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "foodCell", for: indexPath)
cell.textLabel?.text = listOfMeals[indexPath.row].foodname
cell.detailTextLabel?.text = String(listOfMeals[indexPath.row].calories) + "kJ"
cell.detailTextLabel?.textColor = UIColor.secondaryLabel
cell.accessoryType = .disclosureIndicator
return cell
}
我也尝试使用下面的代码来尝试按日期对对象进行排序,但没有成功。
self.listOfMeals.sort { (meal1, meal2) -> Bool in
return (meal1.date!.compare(meal2.date!))
}
我试过这里发布的其他方法,但似乎都不起作用。
我相信 compare
方法不是你所需要的,因为它将返回一个 比较结果 实例在这两个日期之间。另外,如果日期是选项,不要强行投掷,否则当其中一个日期为零时,你的应用程序可能会崩溃。
self.listOfMeals.sort { meal1, meal2 -> Bool in
guard let meal1Date = meal1.date, let meal2Date = meal2.date else { return false }
return meal1Date < meal2Date
}
如果你想一次性对你的对象进行排序和分组,你可以使用 Dictionary(grouping:by:)
连同排序
let calendar = Calendar.current
let nilDate = Date.distantPast
let grouped = Dictionary(grouping: listOfMeals.sorted(by: { ($0.date ?? nilDate) < ($1.date ?? nilDate) }),
by: { calendar.startOfDay(for: $0.date ?? nilDate) })
下面是一个简单的测试案例
struct Meal: CustomStringConvertible {
var id: String?
var date: Date?
var description: String {
get {
let dateString = date == nil ? "nil" : String(describing: date!)
return "\(id!) \(dateString)"
}
}
}
let day = 24.0 * 60.0 * 60.0
var listOfMeals = [
Meal(id: "a", date: Date(timeIntervalSinceNow: 5000)),
Meal(id: "b", date: Date(timeIntervalSinceNow: -5000)),
Meal(id: "c", date: Date(timeIntervalSinceNow: (day + 5000.0))),
Meal(id: "d", date: Date(timeIntervalSinceNow: day)),
Meal(id: "e", date: Date(timeIntervalSinceNow: -day)),
Meal(id: "f", date: Date(timeIntervalSinceNow: -(day - 5000.0))),
Meal(id: "g", date: Date(timeIntervalSinceNow: 0)),
Meal(id: "h", date: Date(timeIntervalSinceNow: -2 * day)),
]
我可能理解错了,但似乎这个问题是问如何读取按日期排序的膳食。
你不会想要像其他答案中显示的那样读取每顿饭菜,然后在内存中对它们进行排序,因为这可能是一个压倒性的数据量。所以请注意,您可以添加 极限 我的代码,以及只显示6月份的饭菜,或只有10餐的时间,例如。
让Firestore为你做这些工作。下面是读取你的饭菜对象降序的代码。
func readMealsSortedDesc() {
let meals = self.db.collection("meals") //self.db points to my Firestore
meals.order(by: "date", descending: true).getDocuments(completion: { snapshot, error in
if let err = error {
print(err.localizedDescription)
return
}
guard let documents = snapshot?.documents else { return }
for doc in documents {
let docId = doc.documentID
let name = doc.get("foodname") as? String ?? "No Food Name"
let date = doc.get("date") as? Timestamp ?? Timestamp()
print(docId, name, date.dateValue())
}
})
}
如果6月有三顿饭(只为6月增加了一个限制),1号一顿,15号一顿,30号一顿,下面是输出结果。
meal_2 Hot Dog 2020-06-30 04:00:00 +0000
meal_1 Burger 2020-06-15 04:00:00 +0000
meal_0 Pizza 2020-06-01 04:00:00 +0000
我不知道你具体想如何组织数据,但你应该有一个dataSource数组,它包含了代表各部分的对象,也包含了部分子数据。像这样
var myDailyMealsArray = [DailyMealStruct]()
struct DailyMealStruct {
var sectionTitle = "this would be a string of the date"
var mealNameArray = [String]()
}
因此,当你的数据从Firebase读取时,将其添加到相应的DailyMealStruct对象中(该对象中包含2020-06-01的所有膳食,然后该对象中包含2020-06-02的所有膳食等)。
从那里,你的部分是myDailyMealsArray中的#个对象,而子数据则来自每个对象内的 mealNameArray。
func numberOfSections(in tableView: UITableView) -> Int {
return self.myDailyMealsArray.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let title = self.myDailyMealsArray[section].sectionTitle
return title
}