分组数组或字典以在 Swift 中向 UITableView 显示部分
Group Array or Dictionary to show sections to UITableView in Swift
我有这个实现:
Invoice - Hold info about Invoices
import Foundation
class Invoice {
var content: String?
var createdAt: Date?
var dateA: Date?
var dateB: Date?
var employee: String?
var expenseCategory: String?
var from: Date?
var id: String?
var ncf: String?
var ncfA: String?
var ncfB: String?
var numeration: Int64
var paymentMethod: String?
var rejectReason: String?
var sessionId: String?
var status: String?
var subtotal: Double
var sync: Bool
var taxExempt: Bool
var taxType: String?
var taxValue: Double
var to: Date?
var total: Double
var totalInvoice: Int64
var typeA: String?
var typeB: String?
var updatedAt: Date?
var user: String?
var toCompany: Company?
var toDocument: [Document]?
var storeId: String?
var incomeType: String?
var expenseType: String?
var toOtherCompany: Company?
var invoiceDate: Date?
var otherTax: Double
var companyId: String?
init(_content: String?, _storeId: String?, _createdAt: Date?, _dateA: Date?, _dateB: Date?, _employee: String?, _expenseCategory: String?,
_from: Date?, _id: String?, _ncf: String?, _ncfA: String?, _ncfB: String?, _numeration: Int64, _paymentMethod: String?,
_rejectReason: String?, _sessionId: String?, _status: String?, _subtotal: Double, _sync: Bool, _taxExempt: Bool, _taxType: String?,
_taxValue: Double, _to: Date?, _total: Double, _totalInvoice: Int64, _typeA: String?, _typeB: String?, _updatedAt: Date?,
_user: String?, _incomeType: String?, _expenseType: String?, _invoiceDate: Date?, _otherTax: Double, _companyId: String?, _toCompany: Company? = nil, _toDocument: [Document]? = nil,
_toOtherCompany: Company? = nil) {
self.content = _content
self.createdAt = _createdAt
self.dateA = _dateA
self.dateB = _dateB
self.employee = _employee
self.expenseCategory = _expenseCategory
self.from = _from
self.id = _id
self.ncf = _ncf
self.ncfA = _ncfA
self.ncfB = _ncfB
self.numeration = _numeration
self.paymentMethod = _paymentMethod
self.rejectReason = _rejectReason
self.sessionId = _sessionId
self.status = _status
self.subtotal = _subtotal
self.sync = _sync
self.taxExempt = _taxExempt
self.taxType = _taxType
self.taxValue = _taxValue
self.to = _to
self.total = _total
self.totalInvoice = _totalInvoice
self.typeA = _typeA
self.typeB = _typeB
self.updatedAt = _updatedAt
self.user = _user
self.toCompany = _toCompany
self.toDocument = _toDocument
self.storeId = _storeId
self.incomeType = _incomeType
self.expenseType = _expenseType
self.toOtherCompany = _toOtherCompany
self.invoiceDate = _invoiceDate
self.otherTax = _otherTax
self.companyId = _companyId
}
// Get the descriptive status
func getDescriptiveStatus() -> String {
switch self.status {
case InvoiceStatus.waiting.toString():
return InvoiceStatus.waiting.description.uppercased()
case InvoiceStatus.processing.toString():
return InvoiceStatus.processing.description.uppercased()
case InvoiceStatus.processed.toString():
return InvoiceStatus.processed.description.uppercased()
case InvoiceStatus.pending_rejection.toString():
return InvoiceStatus.pending_rejection.description.uppercased()
case InvoiceStatus.rejected.toString():
return InvoiceStatus.rejected.description.uppercased()
case InvoiceStatus.posted.toString():
return InvoiceStatus.posted.description.uppercased()
case InvoiceStatus.reviewing.toString():
return InvoiceStatus.reviewing.description.uppercased()
case InvoiceStatus.pending_upload.toString():
return InvoiceStatus.pending_upload.description.uppercased()
default:
return "UNKNOW STATUS"
}
}
// Get status (based in enum)
func getStatus() -> InvoiceStatus? {
switch self.status {
case InvoiceStatus.waiting.toString():
return InvoiceStatus.waiting
case InvoiceStatus.processing.toString():
return InvoiceStatus.processing
case InvoiceStatus.processed.toString():
return InvoiceStatus.processed
case InvoiceStatus.pending_rejection.toString():
return InvoiceStatus.pending_rejection
case InvoiceStatus.rejected.toString():
return InvoiceStatus.rejected
case InvoiceStatus.posted.toString():
return InvoiceStatus.posted
case InvoiceStatus.reviewing.toString():
return InvoiceStatus.reviewing
case InvoiceStatus.pending_upload.toString():
return InvoiceStatus.pending_upload
default:
return nil
}
}
}
InvoiceGroup - Hold info about invoices grouped by header
import Foundation
struct InvoiceGroup {
var header: String
var invoices: [Invoice] = []
}
This function group all invoices by their relative date string using https://github.com/malcommac/SwiftDate framework
func groupInvoicesByDate(Invoices invoices: [Invoice]) -> [InvoiceGroup] {
let grouped = Dictionary(grouping: invoices) {
[=13=].createdAt?.toRelative()
}
let invoiceGroups = grouped.map {
InvoiceGroup(header: [=13=].key!, invoices: [=13=].value)
}
return invoiceGroups
}
To display headers and cells I do this:
extension HistoryViewController : UITableViewDataSource, UITableViewDelegate
{
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return self.invoicesGroups[section].header
}
func numberOfSections(in tableView: UITableView) -> Int {
return self.invoicesGroups.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.invoicesGroups[section].invoices.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let currentInvoice = self.invoicesGroups[indexPath.section].invoices[indexPath.row]
switch currentInvoice.getStatus() {
case .posted:
let cell = (tableView.dequeueReusableCell(withIdentifier: "HistoryItem", for: indexPath) as? HistoryViewCell)!
cell.data(item: currentInvoice, page: self, hidden: !self.isTab0(), selected: self.invoiceSelected)
cell.setCallbackListener(callback: self)
return cell
case .waiting:
let cell = (tableView.dequeueReusableCell(withIdentifier: "HistoryItem", for: indexPath) as? HistoryViewCell)!
cell.data(item: currentInvoice, page: self, hidden: !self.isTab0(), selected: self.invoiceSelected)
cell.setCallbackListener(callback: self)
return cell
case .rejected, .pending_rejection:
let cell = (tableView.dequeueReusableCell(withIdentifier: "HistoryItemReturn", for: indexPath) as? HistoryItemReturn)!
cell.data(item: currentInvoice, page: self)
return cell
case .pending_upload:
let cell = (tableView.dequeueReusableCell(withIdentifier: "HistoryItem", for: indexPath) as? HistoryViewCell)!
cell.data(item: currentInvoice, page: self)
return cell
default:
let cell = (tableView.dequeueReusableCell(withIdentifier: "HistoryItem", for: indexPath) as? HistoryViewCell)!
cell.data(item: currentInvoice, page: self)
return cell
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let currentInvoice = self.invoicesGroups[indexPath.section].invoices[indexPath.row]
switch currentInvoice.getStatus() {
case .posted:
// HistoryItem cell
return 95
case .waiting:
// HistoryItem cell
return 75
case .rejected:
// HistoryItemReturn cell
return 110
case .pending_upload:
return 75
default:
return 100
}
}
}
当我从 CoreData 获取发票时,我将所有发票按 createdAt 字段降序排列
func histories(sessionId: String, types: [InvoiceType], status : [InvoiceStatus], companyId: String, sortDate: String = "createdAt") -> [Invoice] {
var invoiceTypeList: [String] = []
var invoiceStatusList: [String] = []
for invTp in types {
invoiceTypeList.append(invTp.toString())
}
for invSt in status {
invoiceStatusList.append(invSt.toString())
}
var predicates: [NSPredicate] = []
let predicate1 = NSPredicate(format: "status IN %@", invoiceStatusList)
predicates.append(predicate1)
if !types.isEmpty {
let predicate2 = NSPredicate(format: "typeA IN %@", invoiceTypeList)
predicates.append(predicate2)
}
let predicate3 = NSPredicate(format: "sessionId = %@", sessionId as CVarArg)
predicates.append(predicate3)
let predicate4 = NSPredicate(format: "companyId = %@", companyId as CVarArg)
predicates.append(predicate4)
let compoundPredicate = NSCompoundPredicate(type: .and, subpredicates: predicates)
let sortDescriptor = NSSortDescriptor(key: sortDate, ascending: false)
let arrays = _invoiceDataRepository.getAll(predicate: compoundPredicate, sort: [sortDescriptor])
return arrays
}
但是 header 在 TableView 中随机显示所有发票,例如:
{
"Today": [
{
"id": 3,
"ncf": "A1",
"status": "posted"
}
],
"Yesterday": [
{
"id": 2,
"ncf": "B1",
"status": "posted"
}
],
"3 days ago": [
{
"id": 1,
"ncf": "C3",
"status": "posted"
}
]
}
如果我重新加载 TableView,顺序会发生变化,例如:
{
"Today": [
{
"id": 3,
"ncf": "A1",
"status": "posted"
}
],
"3 days ago": [
{
"id": 1,
"ncf": "C3",
"status": "posted"
}
],
"Yesterday": [
{
"id": 2,
"ncf": "B1",
"status": "posted"
}
]
}
或者
{
"3 days ago": [
{
"id": 1,
"ncf": "C3",
"status": "posted"
}
],
"Today": [
{
"id": 3,
"ncf": "A1",
"status": "posted"
}
],
"Yesterday": [
{
"id": 2,
"ncf": "B1",
"status": "posted"
}
]
}
我不知道为什么会这样。我需要 headers 按其元素的相对日期字符串降序排列。
您写道:
let grouped = Dictionary(grouping: invoices) {
[=10=].createdAt?.toRelative()
}
let invoiceGroups = grouped.map {
InvoiceGroup(header: [=10=].key!, invoices: [=10=].value)
}
让我们稍微修改一下以进行调试:
let invoiceGroups = grouped.map {
print("Adding for key: \([=11=].key)")
return InvoiceGroup(header: [=11=].key!, invoices: [=11=].value)
}
问题是 Dictionary
未排序,它是 Key-Value 访问权限,而不是 Index-Value 访问权限。所以不能保证第一个被映射的 key-value 将是较旧的日期,或者是最新的日期,后面的将保持该顺序。
相反,让我们帮助您对它们进行排序。 Sicne toRelative()
将创建一个 String
,而“昨天”和“3 天前”“很难比较”,先保留日期吧:
让我们以dateTruncated(at: [.year,.month,.day])
为例,使同一组中的所有日期均具有不同的时间。或者您可以使用 dateAtStartOf(.day)
。我没有深入研究图书馆,但确实本地和时区可能会导致问题,所以请检查一下。
let grouped = Dictionary(grouping: invoices) {
[=12=].createdAt?.dateTruncated(at: [.year,.month,.day])
}
然后,我们把它整理成元组=
let sortedTuples = grouped.sorted(by: { [=13=].key < .key }
然后,我们可以将元组映射到您的自定义结构中:
let invoiceGroups = sortedTuples.map {
InvoiceGroupe(header: [=14=].key.toRelative, invoices: [=14=].values)
}
我有这个实现:
Invoice - Hold info about Invoices
import Foundation
class Invoice {
var content: String?
var createdAt: Date?
var dateA: Date?
var dateB: Date?
var employee: String?
var expenseCategory: String?
var from: Date?
var id: String?
var ncf: String?
var ncfA: String?
var ncfB: String?
var numeration: Int64
var paymentMethod: String?
var rejectReason: String?
var sessionId: String?
var status: String?
var subtotal: Double
var sync: Bool
var taxExempt: Bool
var taxType: String?
var taxValue: Double
var to: Date?
var total: Double
var totalInvoice: Int64
var typeA: String?
var typeB: String?
var updatedAt: Date?
var user: String?
var toCompany: Company?
var toDocument: [Document]?
var storeId: String?
var incomeType: String?
var expenseType: String?
var toOtherCompany: Company?
var invoiceDate: Date?
var otherTax: Double
var companyId: String?
init(_content: String?, _storeId: String?, _createdAt: Date?, _dateA: Date?, _dateB: Date?, _employee: String?, _expenseCategory: String?,
_from: Date?, _id: String?, _ncf: String?, _ncfA: String?, _ncfB: String?, _numeration: Int64, _paymentMethod: String?,
_rejectReason: String?, _sessionId: String?, _status: String?, _subtotal: Double, _sync: Bool, _taxExempt: Bool, _taxType: String?,
_taxValue: Double, _to: Date?, _total: Double, _totalInvoice: Int64, _typeA: String?, _typeB: String?, _updatedAt: Date?,
_user: String?, _incomeType: String?, _expenseType: String?, _invoiceDate: Date?, _otherTax: Double, _companyId: String?, _toCompany: Company? = nil, _toDocument: [Document]? = nil,
_toOtherCompany: Company? = nil) {
self.content = _content
self.createdAt = _createdAt
self.dateA = _dateA
self.dateB = _dateB
self.employee = _employee
self.expenseCategory = _expenseCategory
self.from = _from
self.id = _id
self.ncf = _ncf
self.ncfA = _ncfA
self.ncfB = _ncfB
self.numeration = _numeration
self.paymentMethod = _paymentMethod
self.rejectReason = _rejectReason
self.sessionId = _sessionId
self.status = _status
self.subtotal = _subtotal
self.sync = _sync
self.taxExempt = _taxExempt
self.taxType = _taxType
self.taxValue = _taxValue
self.to = _to
self.total = _total
self.totalInvoice = _totalInvoice
self.typeA = _typeA
self.typeB = _typeB
self.updatedAt = _updatedAt
self.user = _user
self.toCompany = _toCompany
self.toDocument = _toDocument
self.storeId = _storeId
self.incomeType = _incomeType
self.expenseType = _expenseType
self.toOtherCompany = _toOtherCompany
self.invoiceDate = _invoiceDate
self.otherTax = _otherTax
self.companyId = _companyId
}
// Get the descriptive status
func getDescriptiveStatus() -> String {
switch self.status {
case InvoiceStatus.waiting.toString():
return InvoiceStatus.waiting.description.uppercased()
case InvoiceStatus.processing.toString():
return InvoiceStatus.processing.description.uppercased()
case InvoiceStatus.processed.toString():
return InvoiceStatus.processed.description.uppercased()
case InvoiceStatus.pending_rejection.toString():
return InvoiceStatus.pending_rejection.description.uppercased()
case InvoiceStatus.rejected.toString():
return InvoiceStatus.rejected.description.uppercased()
case InvoiceStatus.posted.toString():
return InvoiceStatus.posted.description.uppercased()
case InvoiceStatus.reviewing.toString():
return InvoiceStatus.reviewing.description.uppercased()
case InvoiceStatus.pending_upload.toString():
return InvoiceStatus.pending_upload.description.uppercased()
default:
return "UNKNOW STATUS"
}
}
// Get status (based in enum)
func getStatus() -> InvoiceStatus? {
switch self.status {
case InvoiceStatus.waiting.toString():
return InvoiceStatus.waiting
case InvoiceStatus.processing.toString():
return InvoiceStatus.processing
case InvoiceStatus.processed.toString():
return InvoiceStatus.processed
case InvoiceStatus.pending_rejection.toString():
return InvoiceStatus.pending_rejection
case InvoiceStatus.rejected.toString():
return InvoiceStatus.rejected
case InvoiceStatus.posted.toString():
return InvoiceStatus.posted
case InvoiceStatus.reviewing.toString():
return InvoiceStatus.reviewing
case InvoiceStatus.pending_upload.toString():
return InvoiceStatus.pending_upload
default:
return nil
}
}
}
InvoiceGroup - Hold info about invoices grouped by header
import Foundation
struct InvoiceGroup {
var header: String
var invoices: [Invoice] = []
}
This function group all invoices by their relative date string using https://github.com/malcommac/SwiftDate framework
func groupInvoicesByDate(Invoices invoices: [Invoice]) -> [InvoiceGroup] {
let grouped = Dictionary(grouping: invoices) {
[=13=].createdAt?.toRelative()
}
let invoiceGroups = grouped.map {
InvoiceGroup(header: [=13=].key!, invoices: [=13=].value)
}
return invoiceGroups
}
To display headers and cells I do this:
extension HistoryViewController : UITableViewDataSource, UITableViewDelegate
{
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return self.invoicesGroups[section].header
}
func numberOfSections(in tableView: UITableView) -> Int {
return self.invoicesGroups.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.invoicesGroups[section].invoices.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let currentInvoice = self.invoicesGroups[indexPath.section].invoices[indexPath.row]
switch currentInvoice.getStatus() {
case .posted:
let cell = (tableView.dequeueReusableCell(withIdentifier: "HistoryItem", for: indexPath) as? HistoryViewCell)!
cell.data(item: currentInvoice, page: self, hidden: !self.isTab0(), selected: self.invoiceSelected)
cell.setCallbackListener(callback: self)
return cell
case .waiting:
let cell = (tableView.dequeueReusableCell(withIdentifier: "HistoryItem", for: indexPath) as? HistoryViewCell)!
cell.data(item: currentInvoice, page: self, hidden: !self.isTab0(), selected: self.invoiceSelected)
cell.setCallbackListener(callback: self)
return cell
case .rejected, .pending_rejection:
let cell = (tableView.dequeueReusableCell(withIdentifier: "HistoryItemReturn", for: indexPath) as? HistoryItemReturn)!
cell.data(item: currentInvoice, page: self)
return cell
case .pending_upload:
let cell = (tableView.dequeueReusableCell(withIdentifier: "HistoryItem", for: indexPath) as? HistoryViewCell)!
cell.data(item: currentInvoice, page: self)
return cell
default:
let cell = (tableView.dequeueReusableCell(withIdentifier: "HistoryItem", for: indexPath) as? HistoryViewCell)!
cell.data(item: currentInvoice, page: self)
return cell
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let currentInvoice = self.invoicesGroups[indexPath.section].invoices[indexPath.row]
switch currentInvoice.getStatus() {
case .posted:
// HistoryItem cell
return 95
case .waiting:
// HistoryItem cell
return 75
case .rejected:
// HistoryItemReturn cell
return 110
case .pending_upload:
return 75
default:
return 100
}
}
}
当我从 CoreData 获取发票时,我将所有发票按 createdAt 字段降序排列
func histories(sessionId: String, types: [InvoiceType], status : [InvoiceStatus], companyId: String, sortDate: String = "createdAt") -> [Invoice] {
var invoiceTypeList: [String] = []
var invoiceStatusList: [String] = []
for invTp in types {
invoiceTypeList.append(invTp.toString())
}
for invSt in status {
invoiceStatusList.append(invSt.toString())
}
var predicates: [NSPredicate] = []
let predicate1 = NSPredicate(format: "status IN %@", invoiceStatusList)
predicates.append(predicate1)
if !types.isEmpty {
let predicate2 = NSPredicate(format: "typeA IN %@", invoiceTypeList)
predicates.append(predicate2)
}
let predicate3 = NSPredicate(format: "sessionId = %@", sessionId as CVarArg)
predicates.append(predicate3)
let predicate4 = NSPredicate(format: "companyId = %@", companyId as CVarArg)
predicates.append(predicate4)
let compoundPredicate = NSCompoundPredicate(type: .and, subpredicates: predicates)
let sortDescriptor = NSSortDescriptor(key: sortDate, ascending: false)
let arrays = _invoiceDataRepository.getAll(predicate: compoundPredicate, sort: [sortDescriptor])
return arrays
}
但是 header 在 TableView 中随机显示所有发票,例如:
{
"Today": [
{
"id": 3,
"ncf": "A1",
"status": "posted"
}
],
"Yesterday": [
{
"id": 2,
"ncf": "B1",
"status": "posted"
}
],
"3 days ago": [
{
"id": 1,
"ncf": "C3",
"status": "posted"
}
]
}
如果我重新加载 TableView,顺序会发生变化,例如:
{
"Today": [
{
"id": 3,
"ncf": "A1",
"status": "posted"
}
],
"3 days ago": [
{
"id": 1,
"ncf": "C3",
"status": "posted"
}
],
"Yesterday": [
{
"id": 2,
"ncf": "B1",
"status": "posted"
}
]
}
或者
{
"3 days ago": [
{
"id": 1,
"ncf": "C3",
"status": "posted"
}
],
"Today": [
{
"id": 3,
"ncf": "A1",
"status": "posted"
}
],
"Yesterday": [
{
"id": 2,
"ncf": "B1",
"status": "posted"
}
]
}
我不知道为什么会这样。我需要 headers 按其元素的相对日期字符串降序排列。
您写道:
let grouped = Dictionary(grouping: invoices) {
[=10=].createdAt?.toRelative()
}
let invoiceGroups = grouped.map {
InvoiceGroup(header: [=10=].key!, invoices: [=10=].value)
}
让我们稍微修改一下以进行调试:
let invoiceGroups = grouped.map {
print("Adding for key: \([=11=].key)")
return InvoiceGroup(header: [=11=].key!, invoices: [=11=].value)
}
问题是 Dictionary
未排序,它是 Key-Value 访问权限,而不是 Index-Value 访问权限。所以不能保证第一个被映射的 key-value 将是较旧的日期,或者是最新的日期,后面的将保持该顺序。
相反,让我们帮助您对它们进行排序。 Sicne toRelative()
将创建一个 String
,而“昨天”和“3 天前”“很难比较”,先保留日期吧:
让我们以dateTruncated(at: [.year,.month,.day])
为例,使同一组中的所有日期均具有不同的时间。或者您可以使用 dateAtStartOf(.day)
。我没有深入研究图书馆,但确实本地和时区可能会导致问题,所以请检查一下。
let grouped = Dictionary(grouping: invoices) {
[=12=].createdAt?.dateTruncated(at: [.year,.month,.day])
}
然后,我们把它整理成元组=
let sortedTuples = grouped.sorted(by: { [=13=].key < .key }
然后,我们可以将元组映射到您的自定义结构中:
let invoiceGroups = sortedTuples.map {
InvoiceGroupe(header: [=14=].key.toRelative, invoices: [=14=].values)
}