Składane sekcje: [Assert] Nie można ustalić nowego globalnego indeksu wierszy dla preReloadFirstVisibleRow (0)

9

Implementuję zwijalne nagłówki sekcji w UITableViewController.

Oto jak określam, ile wierszy ma być wyświetlanych w poszczególnych sekcjach:

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

Istnieje struktura, która przechowuje informacje o sekcji z wartością logiczną dla „isCollapsed”.

Oto jak przełączam ich stany:

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()
}

Wszystko działa i ładnie animuje, jednak w konsoli podczas zwijania rozwiniętej sekcji otrzymuję [Assert]:

[Assert] Nie można ustalić nowego globalnego indeksu wierszy dla preReloadFirstVisibleRow (0)

Dzieje się tak, niezależnie od tego, czy jest to ta sama otwarta sekcja, zamykanie (zwijanie), czy też otwieram inną sekcję i „automatycznie zamykam” poprzednio otwartą sekcję.

Nic nie robię z danymi; to jest trwałe.

Czy ktoś mógłby wyjaśnić, czego brakuje? Dzięki

iOSProgrammingIsFun
źródło
Czy Twój widok na tabelę składa się z kilku sekcji i niezbyt wielu wierszy?
Byron Coetsee,
Czy kiedykolwiek udało ci się to naprawić?
PaulDoesDev
@ByronCoetsee Tak, dopóki sekcja nie zostanie rozwinięta. Więc kiedy wszystko się zawali, są to tylko nagłówki sekcji. Kiedy jeden jest rozwinięty, wszystkie nagłówki sekcji dla nierozwiniętych sekcji i nagłówek sekcji, a następnie komórki danych.
iOSProgrammingIsFun
@PaulDoesDev Zrobiłem to, ale nie przy użyciu tego mechanizmu. Całkowicie przepisałem go tak, aby chociaż wyglądał tak samo, działa zupełnie inaczej. Jednak zostawię to tutaj, na wypadek, gdyby ktoś mógł to elegancko naprawić lub w jakiś sposób pomaga innym.
iOSProgrammingIsFun
1
@ iProgramowanie IsFun haha ​​tak, myślałem, że to może być hack i technicznie jest, ale ilość kodu i fakt, że jest całkiem czysty, oznacza, że ​​mogę pozwolić sobie spać w nocy: kod P opublikowany poniżej
Byron Coetsee

Odpowiedzi:

8

Aby tableView wiedział, gdzie jest, podczas przeładowywania wierszy itp., Próbuje znaleźć „wiersz kontrolny”, który wykorzystuje jako odniesienie. To się nazywa preReloadFirstVisibleRow. Ponieważ tableView może nie mieć w dowolnym momencie żadnych widocznych wierszy ze względu na zwinięcie wszystkich sekcji, tableView może się mylić, ponieważ nie może znaleźć kotwicy. Następnie zresetuje się do góry.

Rozwiązanie: Dodaj wiersz o wysokości 0 do każdej zwiniętej grupy. W ten sposób, nawet jeśli sekcja jest zwinięta, nadal jest obecny rząd (choć o wysokości 0px). TableView zawsze ma wtedy coś do zaczepienia jako odniesienie. Zobaczysz to po dodaniu wiersza, numberOfRowsInSectionjeśli liczba wierszy wynosi 0, i obsłużeniu wszelkich dalszych indexPath.rowwywołań, upewniając się, że zwracana jest wartość komórki Phatom, zanim indexPath.rowbędzie to konieczne, jeśli datasource.visibleRowswynosi 0.

Łatwiej jest demo w kodzie:

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
źródło