Развертываемые разделы: [Assert] Невозможно определить новый глобальный индекс строки для preReloadFirstVisibleRow (0)

9

Я реализую сворачиваемые заголовки разделов в UITableViewController.

Вот как я определяю, сколько строк показывать на раздел:

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

Существует структура, которая содержит информацию о разделе с помощью bool для isCollapsed.

Вот как я переключаю их состояния:

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

Все хорошо работает и анимируется, однако в консоли при свертывании расширенного раздела я получаю [Assert]:

[Assert] Невозможно определить новый глобальный индекс строки для preReloadFirstVisibleRow (0)

Это происходит независимо от того, является ли это тем же открытым разделом, закрывается (сворачивается) или я открываю другой раздел и автоматически закрываю ранее открытый раздел.

Я ничего не делаю с данными; это постоянно.

Может ли кто-нибудь помочь объяснить, чего не хватает? Спасибо

iOSProgrammingIsFun
источник
Ваше табличное представление составлено из группы разделов и не много фактических строк?
Байрон
Вам когда-нибудь удавалось это исправить?
PaulDoesDev
@ByronCoetsee Да, пока раздел не будет расширен. Поэтому, когда все рухнуло, это просто заголовки разделов. Когда один раскрывается, это все заголовки разделов для нерасширенных разделов и заголовок раздела, а затем ячейки для данных.
iOSProgrammingIsFun
@PaulDoesDev Я сделал, но не с помощью этого механизма. Я полностью переписал его, чтобы он выглядел одинаково, но работал совершенно по-другому. Однако я собираюсь оставить это здесь на случай, если кто-то сможет это элегантно исправить, или это каким-то образом поможет другим.
iOSProgrammingIsFun
1
@iOSProgrammingIsFun хаха, да, я думал, что это может выглядеть как хак, и технически это так, но количество кода и тот факт, что он на самом деле довольно чистый, означает, что я могу позволить себе спать по ночам: код P, опубликованный ниже
Байрон

Ответы:

8

Чтобы tableView узнал, где он находится, пока он перезагружает строки и т. Д., Он пытается найти «якорную строку», которую он использует в качестве ссылки. Это называется preReloadFirstVisibleRow. Так как этот tableView может не иметь видимых строк в какой-то момент из-за того, что все разделы свернуты, tableView будет сбит с толку, так как не может найти привязку. Затем он будет сброшен на вершину.

Решение: добавьте строку высоты 0 к каждой свернутой группе. Таким образом, даже если секция свернута, все еще присутствует строка (хотя и с высотой 0px). В этом случае tableView всегда есть что-то, на что можно ссылаться. Вы увидите это в действительности, добавив строку в, numberOfRowsInSectionесли rowcount равен 0, и обработав любые дальнейшие indexPath.rowвызовы, убедившись, что необходимо вернуть значение ячейки phatom прежде, indexPath.rowесли значение datasource.visibleRowsравно 0.

Демо проще в коде:

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
}
Байрон Кётзее
источник