Прежде всего, 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
методы можно вызывать очень часто, поэтому вы должны их эффективно реализоватьtly.
Таким образом, создание фильтрованного массива при каждом вызове не рекомендуется.Лучше обновлять allEventsInVisibleMonth
только тогда, когда:
myCalendarEvents
обновлено (не включено в мои коды выше) selectedDate
обновлено (didSet
в * 1044)* сделает это)
Немного долго, но я верю, что стоит попробовать.