Sections réductibles: [Assert] Impossible de déterminer le nouvel index de ligne global pour preReloadFirstVisibleRow (0)

9

J'implémente des en-têtes de section réductibles dans un UITableViewController.

Voici comment je détermine le nombre de lignes à afficher par section:

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

Il y a une structure qui contient les informations de section avec un booléen pour 'isCollapsed'.

Voici comment je change leurs états:

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

Tout fonctionne et s'anime bien, mais dans la console lors de la réduction d'une section développée, j'obtiens ceci [Assert]:

[Assert] Impossible de déterminer le nouvel index de ligne global pour preReloadFirstVisibleRow (0)

Cela se produit, qu'il s'agisse de la même section ouverte, de la fermeture (repli) ou si j'ouvre une autre section et que je ferme automatiquement la section précédemment ouverte.

Je ne fais rien avec les données; c'est persistant.

Quelqu'un pourrait-il aider à expliquer ce qui manque? Merci

iOSProgrammingIsFun
la source
Votre table est-elle composée d'un tas de sections et pas de nombreuses lignes réelles?
Byron Coetsee
Avez-vous déjà réussi à résoudre ce problème?
PaulDoesDev
@ByronCoetsee Oui, jusqu'à ce qu'une section soit développée. Donc, quand tout s'est effondré, ce ne sont que des en-têtes de section. Quand un est développé, ce sont tous les en-têtes de section pour les sections non développées et un en-tête de section, puis les cellules pour les données.
iOSProgrammingIsFun
@PaulDoesDev Je l'ai fait, mais pas en utilisant ce mécanisme. Je l'ai complètement réécrit pour que, même s'il semble le même, il fonctionne complètement différemment. Cependant, je vais laisser cela ici au cas où quelqu'un pourrait résoudre ce problème avec élégance ou aider les autres d'une manière ou d'une autre.
iOSProgrammingIsFun
1
@iOSProgrammingIsFun haha ​​ouais je pensais que cela pouvait ressembler à un hack et c'est techniquement le cas, mais la quantité de code et le fait qu'il soit en fait assez propre signifie que je peux me laisser dormir la nuit: code P affiché ci
Byron Coetsee

Réponses:

8

Pour qu'une tableView sache où elle se trouve lorsqu'elle recharge des lignes, etc., elle essaie de trouver une "ligne d'ancrage" qu'elle utilise comme référence. C'est ce qu'on appelle un preReloadFirstVisibleRow. Étant donné que cette tableView peut ne pas avoir de lignes visibles à un moment donné en raison de la réduction de toutes les sections, la tableView sera confuse car elle ne peut pas trouver d'ancre. Il sera ensuite réinitialisé au sommet.

Solution: ajoutez une ligne de hauteur 0 à chaque groupe réduit. De cette façon, même si une section est réduite, il y a toujours une ligne présente (bien que de 0px de hauteur). La tableView a alors toujours quelque chose à laquelle se connecter comme référence. Vous verrez cela en effet par l'ajout d'une ligne numberOfRowsInSectionsi le nombre de lignes est 0 et la gestion de tout autre indexPath.rowappel en vous assurant de renvoyer la valeur de la cellule phatom avant indexPath.rowest nécessaire si la valeur datasource.visibleRowsest 0.

Il est plus facile de faire une démonstration dans le code:

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
la source