Как получить количество для numberOfRows, когда количество строк в каждом разделе меняется? - PullRequest
0 голосов
/ 03 октября 2018

Я пытаюсь заполнить табличное представление записями из файла XML.До сих пор я анализировал XML-файл в Struct и записывал методы для titleForHeaderInSection и numberOfSections, но я пытаюсь вычислить numberOfRows для каждого раздела путем подсчета <holiday> записей в каждом <calendarevent>.Я думаю, что моя главная борьба заключается в непонимании того, как использовать [раздел].Вот что я сделал до сих пор:

Я проанализировал XML-файл с календарными датами и подобными событиями.Вы увидите, как у первого календарного события есть 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 и добавления его в структуру я начинаю получать значения для табличного представления.

Каждый раздел состоит из одного <calendarevent>, если <month> совпадает с текущим месяцем Дата.XML имеет <calendarevent> из других месяцев, поэтому я не хочу их всех.Я проверяю, является ли текущий месяц == месяцем в XML, если это так, то я считаю число # событий даты для numberOfSections и форматирую события даты, чтобы они стали заголовками этих разделов.

Затем каждая строка в разделе представляетодин <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> - вот где я застрял.

Для 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
    }

1 Ответ

0 голосов
/ 04 октября 2018

Прежде всего, 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)* сделает это)

Немного долго, но я верю, что стоит попробовать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...