Seções recolhíveis: [Assert] Não é possível determinar o novo índice global de linhas para preReloadFirstVisibleRow (0)

9

Estou implementando cabeçalhos de seção recolhíveis em um UITableViewController.

Veja como eu determino quantas linhas mostrar por seção:

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

Existe uma estrutura que mantém as informações da seção com um booleano para 'isCollapsed'.

Veja como estou alternando seus estados:

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

Tudo está funcionando e animando muito bem, no entanto, no console ao recolher uma seção expandida, recebo o seguinte [Assert]:

[Assert] Não foi possível determinar o novo índice de linha global para preReloadFirstVisibleRow (0)

Isso acontece, independentemente de ser a mesma seção aberta, fechar (fechar) ou se estou abrindo outra seção e 'fechando automaticamente' a seção aberta anteriormente.

Não estou fazendo nada com os dados; isso é persistente.

Alguém poderia ajudar a explicar o que está faltando? obrigado

iOSProgrammingIsFun
fonte
A sua tableview é composta de várias seções e poucas linhas reais?
Byron Coetsee
Você já conseguiu consertar isso?
PaulDoesDev
@ByronCoetsee Sim, até que uma seção seja expandida. Então, quando tudo desmoronou, são apenas os cabeçalhos das seções. Quando um é expandido, todos os cabeçalhos de seção para as seções não expandidas e um cabeçalho de seção e células para dados.
iOSProgrammingIsFun
@PaulDoesDev eu fiz, mas não usando esse mecanismo. Eu o reescrevi completamente para que, embora pareça o mesmo, funcione de maneira completamente diferente. No entanto, vou deixar isso aqui, caso alguém possa resolver isso com elegância ou ajudar os outros de alguma forma.
iOSProgrammingIsFun
11
@iOSProgrammingIsFun haha ​​sim, eu pensei que pode parecer um hack e tecnicamente é, mas a quantidade de código e o fato de que é realmente bastante limpo significa que eu posso me permitir dormir à noite: código P publicado abaixo
Byron Coetsee

Respostas:

8

Para que um tableView saiba onde está enquanto recarrega as linhas, etc, ele tenta encontrar uma "linha âncora" que ele usa como referência. Isso é chamado de preReloadFirstVisibleRow. Como esse tableView pode não ter nenhuma linha visível em algum momento devido a todas as seções serem recolhidas, o tableView ficará confuso, pois não consegue encontrar uma âncora. Ele será redefinido para o topo.

Solução: adicione uma linha de altura 0 a cada grupo que foi recolhido. Dessa forma, mesmo que uma seção seja recolhida, ainda há uma linha presente (embora com 0px de altura). O tableView sempre tem algo para se conectar como referência. Você verá isso com efeito, adicionando uma linha numberOfRowsInSectionse o número de linhas for 0 e lidando com outras indexPath.rowchamadas, certificando-se de retornar o valor da célula phatom antes que indexPath.rowseja necessário se datasource.visibleRowsfor 0.

É mais fácil fazer uma demonstração no código:

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
fonte