Ausklappbare Abschnitte: [Assert] Neuer globaler Zeilenindex für preReloadFirstVisibleRow (0) kann nicht ermittelt werden

9

Ich implementiere reduzierbare Abschnittsüberschriften in einem UITableViewController.

So bestimme ich, wie viele Zeilen pro Abschnitt angezeigt werden sollen:

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
    return self.sections[section].isCollapsed ? 0 : self.sections[section].items.count
}

Es gibt eine Struktur, die die Abschnittsinformationen mit einem Bool für 'isCollapsed' enthält.

So schalte ich ihre Zustände um:

private func getSectionsNeedReload(_ section: Int) -> [Int]
{
    var sectionsToReload: [Int] = [section]

    let toggleSelectedSection = !sections[section].isCollapsed

    // Toggle collapse
    self.sections[section].isCollapsed = toggleSelectedSection

    if self.previouslyOpenSection != -1 && section != self.previouslyOpenSection
    {
        self.sections[self.previouslyOpenSection].isCollapsed = !self.sections[self.previouslyOpenSection].isCollapsed
        sectionsToReload.append(self.previouslyOpenSection)
        self.previouslyOpenSection = section
    }
    else if section == self.previouslyOpenSection
    {
        self.previouslyOpenSection = -1
    }
    else
    {
        self.previouslyOpenSection = section
    }

    return sectionsToReload
}



internal func toggleSection(_ header: CollapsibleTableViewHeader, section: Int)
{
    let sectionsNeedReload = getSectionsNeedReload(section)

    self.tableView.beginUpdates()
    self.tableView.reloadSections(IndexSet(sectionsNeedReload), with: .automatic)
    self.tableView.endUpdates()
}

Alles funktioniert und animiert gut, aber in der Konsole beim Ausblenden eines erweiterten Abschnitts erhalte ich Folgendes: [Assert]:

[Assert] Neuer globaler Zeilenindex für preReloadFirstVisibleRow (0) kann nicht ermittelt werden

Dies geschieht unabhängig davon, ob es sich um denselben geöffneten Abschnitt handelt, der geschlossen (reduziert) wird oder ob ich einen anderen Abschnitt öffne und den zuvor geöffneten Abschnitt automatisch schließe.

Ich mache nichts mit den Daten; das ist hartnäckig.

Könnte jemand helfen zu erklären, was fehlt? Vielen Dank

iOSProgrammingIsFun
quelle
Besteht Ihre Tabellenansicht aus mehreren Abschnitten und nicht aus vielen tatsächlichen Zeilen?
Byron Coetsee
Haben Sie es jemals geschafft, dies zu beheben?
PaulDoesDev
@ByronCoetsee Ja, bis ein Abschnitt erweitert wird. Wenn also alle zusammengebrochen sind, sind es nur Abschnittsüberschriften. Wenn einer erweitert wird, sind dies alle Abschnittsüberschriften für die nicht erweiterten Abschnitte und eine Abschnittsüberschrift und dann Zellen für Daten.
iOSProgrammingIsFun
@PaulDoesDev Ich habe es getan, aber nicht mit diesem Mechanismus. Ich habe es komplett neu geschrieben, so dass es zwar gleich aussieht, aber völlig anders funktioniert. Ich werde dies jedoch hier belassen, falls jemand dies elegant beheben kann oder es anderen irgendwie hilft.
iOSProgrammingIsFun
1
@iOSProgrammingIsFun haha ​​yeah Ich dachte, es könnte sich wie ein Hack anfühlen und es ist technisch, aber die Menge an Code und die Tatsache, dass es tatsächlich ziemlich sauber ist, bedeutet, dass ich mich nachts schlafen lassen kann: P-Code unten gepostet
Byron Coetsee

Antworten:

8

Damit eine tableView weiß, wo sie sich befindet, während sie Zeilen usw. neu lädt, versucht sie, eine "Ankerzeile" zu finden, die sie als Referenz verwendet. Dies nennt man apreReloadFirstVisibleRow . Da diese tableView möglicherweise irgendwann keine sichtbaren Zeilen mehr hat, weil alle Abschnitte reduziert sind, wird die tableView verwirrt, da sie keinen Anker finden kann. Es wird dann nach oben zurückgesetzt.

Lösung: Fügen Sie jeder Gruppe, die reduziert ist, eine Zeile mit der Höhe 0 hinzu. Auf diese Weise ist auch dann noch eine Zeile vorhanden, wenn ein Abschnitt reduziert ist (wenn auch mit einer Höhe von 0 Pixel). Die tableView hat dann immer etwas als Referenz. Sie sehen dies, indem Sie eine Zeile in numberOfRowsInSectionhinzufügen, wenn die Zeilenanzahl 0 ist, und weitere indexPath.rowAufrufe bearbeiten, indem Sie sicherstellen, dass der Phatom-Zellenwert zurückgegeben wird, bevor er indexPath.rowbenötigt wird, wenn diedatasource.visibleRows 0 ist.

Es ist einfacher, im Code zu demonstrieren:

func numberOfSections(in tableView: UITableView) -> Int {
    return datasource.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return datasource[section].visibleRows.count == 0 ? 1 : datasource[section].visibleRows.count
}

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    datasource[section].section = section
    return datasource[section]
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    if datasource[indexPath.section].visibleRows.count == 0 { return 0 }
    return datasource[indexPath.section].visibleRows[indexPath.row].bounds.height
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    if datasource[indexPath.section].visibleRows.count == 0 { return UITableViewCell() }

    // I've left this stuff here to show the real contents of a cell - note how
    // the phantom cell was returned before this point.

    let section = datasource[indexPath.section]
    let cell = TTSContentCell(withView: section.visibleRows[indexPath.row])
    cell.accessibilityLabel = "cell_\(indexPath.section)_\(indexPath.row)"
    cell.accessibilityIdentifier = "cell_\(indexPath.section)_\(indexPath.row)"
    cell.showsReorderControl = true
    return cell
}
Byron Coetsee
quelle