ios - 当每个部分中的行数不同时,如何获取 numberOfRows 的计数?
问题描述
我正在尝试使用 XML 文件中的条目填充表格视图。到目前为止,我已经将 XML 文件解析为 Struct 并编写了 titleForHeaderInSection 和 numberOfSections 的方法,但我正在努力通过计算<holiday>
每个<calendarevent>
. 我认为我的主要困难来自不了解如何使用 [section]。这是我到目前为止所做的:
我已经解析了一个 xml 文件,其中包含这样的日历日期和事件。您将看到第一个 calendarevent 有 2 个假期和描述条目,但第二个有 1 个。:
<calendar>
<calendarevent>
<month>October</month>
<dateevent>2018 10 01</dateevent>
<datenumber>01</datenumber>
<holiday>First Holiday</holiday>
<description>aaaaaaaaaa</description>
<holiday>Second Holiday</holiday>
<description>bbbbbbbbbb</description>
</calendarevent>
<calendarevent>
<month>October</month>
<dateevent>2018 10 10</dateevent>
<datenumber>10</datenumber>
<holiday>Third Holiday</holiday>
<description>ccccccccccc</description>
</calendarevent>
.... and so on
对于一个看起来像这样的结构:
struct CalendarDates {
struct CalendarEvents {
var month = ""
var eventdate = ""
var eventdatenumber = ""
var holiday = ""
var description = ""
}
}
这是我的 XML 解析器代码:
class CalendarViewController {
var myCalendarDatesFromStrut = [CalendarDates]()
var myCalendarEventsFromStrut = [CalendarDates.CalendarEvents]()
}
extension CalendarViewController {
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
calendarEventsElementFromXML = elementName
}
func parser(_ parser: XMLParser, foundCharacters string: String) {
let data = string.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
if data.count != 0 {
switch calendarEventsElementFromXML
{
case "month": monthsFromXML = data
case "dateevent": eventdatesFromXML = data
case "datenumber": eventdatenumbersFromXML = data
case "holiday": holidaysFromXML = data
default: break
}
}
}
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
if elementName == "calendarevent" {
var myCalendarDates = CalendarDates.CalendarEvents()
myCalendarDates.month = monthsFromXML
myCalendarDates.eventdate = eventdatesFromXML
myCalendarDates.eventdatenumber = eventdatenumbersFromXML
myCalendarDates.holiday = holidaysFromXML
myCalendarEventsFromStrut.append(myCalendarDates)
}
}
}
因此,在解析 XML 并将其附加到结构之后,我开始获取 tableview 的值。
<calendarevent>
如果<month>
与当前日期月份相同,则每个部分都由一个部分组成。XML<calendarevent>
来自其他月份,所以我不想要所有这些。我检查当前月份是否在 XML 中为 == 月份,如果是,那么我计算 numberOfSections 的 #of dateevents 并将 dateevents 格式化为这些部分的标题。
然后一节中的每一行代表<holiday>
相应的<calendarevent>
.
extension CalendarViewController: UITableViewDelegate, UITableViewDataSource {
//Calculates # of <dateevents> in current month
func numberOfSections(in tableView: UITableView) -> Int {
formatter.dateFormat = "MMMM"
let currentMonthShown = formatter.string(from: selectedDate)
let allEventsInVisibleMonth = myCalendarEventsFromStrut.filter({ $0.month == currentMonthShown }).map {$0.eventdate}.count
return allEventsInVisibleMonth
}
// Prints <dateevent> reformatted as section header
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
formatter.dateFormat = "MMMM"
let currentMonthShown = formatter.string(from: selectedDate)
let allEventsInVisibleMonth = myCalendarEventsFromStrut.filter({ $0.month == currentMonthShown }).map {$0.eventdate}
formatter.dateFormat = "yyyy-MM-dd"
let eventStringsToDate = allEventsInVisibleMonth.map{ formatter.date(from: $0) }
formatter.dateFormat = "yyyy-MM-dd"
formatter.dateStyle = .long
formatter.timeStyle = .none
let eventDatesBackToString = eventStringsToDate.map{ formatter.string(from: $0 as! Date )}
return eventDatesBackToString[section]
}
现在,一个部分中的每一行都需要代表<holiday>
相应的一个<calendarevent>
- 这就是我卡住的地方。
对于 tableview 的 numberOfRowsInSection,我需要计算<holiday>
每个条目中的 # 个条目,<calendarevent>
并显示每个部分中的 # 行。然后在 cellForRowAt 中打印。有人可以给我一个提示这里的逻辑吗?
这是我尝试过的以及我具体失败的地方:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
formatter.dateFormat = "MMMM"
let currentMonthShown = formatter.string(from: selectedDate)
let allEventsInMonth = myCalendarEventsFromStrut.filter({ $0.month == currentMonthShown }).map {$0.holiday}
return allEventsInMonth.count //This gets me same number of rows in all sections = total # of events in current month.
//I'm struggling to figure out how to get # of events at a particular section, so I can make that the # of rows
}
解决方案
首先,XML 可能holiday
在单个 中包含多个元素calendarevent
,因此您需要更新您CalendarEvents
的以便能够容纳多个假期。
struct Holiday {
var title: String = ""
var description: String = ""
}
//You should better avoid plural form for something which represents a single object
struct CalendarEvent {
var month: String = ""
var eventdate: String = ""
var eventdatenumber: String = ""
//Better use plural form for the name representing multiple objects
var holidays: [Holiday] = []
}
(我删除了外部结构CalendarDates
,因为我找不到任何需要嵌套类型的理由。另外我建议你避免使用复数形式来命名代表单个对象的东西。)
要使用结构解析 XML,您需要使用更多属性XMLParserDelegate
:
class CalendarViewController: UIViewController {
//`myCalendarEventsFromStrut` is too long and `FromStrut` does not make sense
var myCalendarEvents: [CalendarEvent] = []
//Properties needed for parsing your XML
var textCurrentlyParsed: String? = nil
var monthFromXML: String = ""
var dateeventFromXML: String = ""
var datenumberFromXML: String = ""
var holidaysFromXML: [Holiday] = []
//Your table view shows some selected evnets in `myCalendarEvents`,
//To improve response, you should beter keep the filtered result, when `selectedDate` is updated.
var selectedDate: Date? {
didSet {
if let date = selectedDate {
let currentMonthShown = monthFormatter.string(from: date)
allEventsInVisibleMonth = myCalendarEvents.filter({ $0.month == currentMonthShown })
} else {
allEventsInVisibleMonth = [] //Or you prefer `allEventsInVisibleMonth = myCalendarEvents`?
}
}
}
var allEventsInVisibleMonth: [CalendarEvent] = []
//You may have this sort of constants somewhere, this is just an example
let TheCellID = "cell" //Change this according to your actual setups
//
// Date formatters.
// Better keep distinct DateFormaters accoding to the format to avoid simple mistakes
//
let monthFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "MMMM"
return formatter
}()
let dateeventFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "yyyy MM dd"
return formatter
}()
let sectionHeaderFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd"
return formatter
}()
//...
}
(上面的代码有一些补充,可以写一些UITableViewDataSource
方法的例子。)
使用上面的属性,您可以编写XMLParserDelegate
如下方法:
extension CalendarViewController: XMLParserDelegate {
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
switch elementName {
case "month", "dateevent", "datenumber", "holiday", "description":
//Reset the text content for the element
textCurrentlyParsed = ""
case "calendarevent":
//Reset all variables which may contain the result of previous element
monthFromXML = ""
dateeventFromXML = ""
datenumberFromXML = ""
holidaysFromXML = []
case "calendar":
//Can be ignored
break
default:
print("Unexpected start tag:", elementName)
break
}
}
func parser(_ parser: XMLParser, foundCharacters string: String) {
//`parser(_:foundCharacters:)` may be called several times for a seemingly single text content,
//So you need to add the `string` to the currently parsed text
textCurrentlyParsed? += string
}
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
switch elementName {
case "calendarevent":
var calendarEvent = CalendarEvent()
calendarEvent.month = monthFromXML
calendarEvent.eventdate = dateeventFromXML
calendarEvent.eventdatenumber = datenumberFromXML
calendarEvent.holidays = holidaysFromXML
myCalendarEvents.append(calendarEvent)
case "month":
if let parsedText = textCurrentlyParsed?.trimmingCharacters(in: .whitespacesAndNewlines) {
monthFromXML = parsedText
}
case "dateevent":
if let parsedText = textCurrentlyParsed?.trimmingCharacters(in: .whitespacesAndNewlines) {
dateeventFromXML = parsedText
}
case "datenumber":
if let parsedText = textCurrentlyParsed?.trimmingCharacters(in: .whitespacesAndNewlines) {
datenumberFromXML = parsedText
}
case "holiday":
if let parsedText = textCurrentlyParsed?.trimmingCharacters(in: .whitespacesAndNewlines) {
var holiday = Holiday()
holiday.title = parsedText
holidaysFromXML.append(holiday)
}
break
case "description":
if
let parsedText = textCurrentlyParsed?.trimmingCharacters(in: .whitespacesAndNewlines),
case let lastIndexOfHoliday = holidaysFromXML.count - 1, lastIndexOfHoliday >= 0
{
//You need to modify the last entry in `holidaysFromXML`
holidaysFromXML[lastIndexOfHoliday].description = parsedText
}
default:
print("Unexpected end tag:", elementName)
break
}
}
}
(如果你不需要trim
文字,上面的代码可以简化一点。)
包含这些代码后,您的UITableViewDataSource
方法将如下所示:
extension CalendarViewController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return allEventsInVisibleMonth.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return allEventsInVisibleMonth[section].holidays.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let eventForTheSection = allEventsInVisibleMonth[indexPath.section]
let holidayForTheRow = eventForTheSection.holidays[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: TheCellID, for: indexPath)
//... setup the cell using `eventForTheSection` and `holidayForTheRow`
return cell
}
// Prints <dateevent> reformatted as section header
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let eventForTheSection = allEventsInVisibleMonth[section]
if let eventdateAsDate = dateeventFormatter.date(from: eventForTheSection.eventdate) {
return sectionHeaderFormatter.string(from: eventdateAsDate)
} else {
return "Broken eventdate: \(eventForTheSection.eventdate)"
}
}
}
(未经测试,您可能需要一些修复。)
通常,UITableViewDataSource
方法可以被非常频繁地调用,因此您应该有效地实现它们。
因此,不建议在每次调用时创建过滤数组。您最好allEventsInVisibleMonth
只在以下情况下更新:
myCalendarEvents
已更新(未包含在我上面的代码中)selectedDate
已更新(didSet
会这样做selectedDate
)
有点长,但我相信值得一试。
推荐阅读
- php - How to get min and max values of the custom numeric meta field?
- python - 使用 Django 将目录从 Google Cloud Storage Bucket 递归复制到另一个 Google Cloud Storage Bucket
- jquery - preventDefault still refreshing the page
- localhost - Remote GUI with LTK doesn't open window
- django - Django如何在信号中保存对象状态?
- ruby - Prepending dash on a Ruby string doesn't freeze the string object?
- docker - Error: ENOENT: no such file or directory, scandir during docker-compose up
- ansible - 在 jinja2 循环中使用主机变量的动态变量
- azure-devops - Terraform ip configuration not found error
- reactjs - How to use a select with options instead two buttons for onClick event ReactJS