27 votes

Animation de mise en page de flux glitch lors de l'insertion de plusieurs vues supplémentaires personnalisées

Lors de l'insertion de plusieurs vues supplémentaires personnalisées dans une vue de collection avec des vues supplémentaires existantes, en utilisant une sous-classe de UICollectionViewFlowLayout, la collection semble créer des animations défectueuses ou ne gérer correctement les insertions.

L'insertion d'une vue supplémentaire se comporte comme prévu, mais l'insertion de deux vues supplémentaires en même temps entraîne un glitch visuel évident, et l'insertion de nombreuses vues supplémentaires en même temps (par exemple 4+) aggrave le problème. (iOS 10.2, iOS 9.3, Swift 3, Xcode 8.2.1)

Voici une démonstration:

Animation montrant deux éléments et deux vues supplémentaires insérés dans une vue de collection avec une disposition de flux simple.

Vous pouvez reproduire le problème en utilisant ce projet exemple. Dans cet exemple simplifié, il y a une vue supplémentaire pour chaque élément, et deux éléments (avec deux vues supplémentaires) sont insérés lors de chaque mise à jour par lots. Dans mon projet réel, j'ai moins de vues supplémentaires que d'éléments et j'utilise d'autres fonctionnalités de mise en page.

Ma meilleure supposition est que quelque chose cause la confusion de la vue de la collection entre les index ou les attributs de mise en page correspondant aux vues supplémentaires existantes/insérées. S'il s'avère que c'est un bogue dans l'une des classes d'Apple, je vous serais très reconnaissant pour votre solution la plus élégante.

Solutions tentées

J'ai essayé et vérifié chacune des solutions suivantes:

  • Mise en oeuvre de indexPathsToInsertForSupplementaryView(ofKind:)—pour autant que je puisse dire, c'est simple et mon implémentation renvoie les index paths corrects. Les vues sont toujours insérées. Pour les vues supplémentaires d'en-tête et de pied de page, UICollectionViewFlowLayout semble renvoyer un tableau trié d'index paths, mais trier le tableau n'a pas fait de différence pour moi.

  • Fournir des attributs de mise en page initiaux et finaux—appeler super pour ces méthodes semble renvoyer les bons cadres, donc il n'est pas évident quels ajustements (le cas échéant) pourraient être apportés aux attributs de mise en page initiaux ou finaux pour résoudre le problème. Mais voir la troisième curiosité notée ci-dessous.

  • Invalidation de la mise en page—cela semble ne faire aucune différence que la mise en page soit invalide manuellement en tout ou en partie avant, pendant ou après performBatchUpdates(), ou pas du tout.

  • Index paths personnalisés—bien que les index paths pour les éléments supplémentaires n'aient pas besoin de correspondre aux index paths des éléments, autant que je puisse dire, et malgré la documentation contraire, UICollectionViewFlowLayout plantera (en demandant des attributs de mise en page pour des index paths inexistants) à moins que les éléments supplémentaires du même type ne soient numérotés séquentiellement à partir de 0. Je suppose que c'est ainsi que la vue de collection et/ou l'objet de mise en page est capable de calculer de nouveaux index paths lorsque des éléments sont insérés ou supprimés (c'est-à-dire en incrémentant ou en décrémentant la propriété item de l'index path). La construction d'index paths arbitraires n'est pas une option.

Curiosités remarquées lors du débogage

  1. Les glitches visuels sont spécifiques aux vues supplémentaires et ne se produisent pas pour les éléments insérés, même lorsque les index paths et les cadres sont les mêmes pour les deux.

  2. Le débogage de la hiérarchie des vues révèle que, bien que les cadres des vues supplémentaires insérées soient corrects, les cadres de certaines vues supplémentaires existantes ne sont pas corrects. Cela malgré le fait que les cadres renvoyés par l'objet de mise en page pour les vues existantes à ces index paths semblent être corrects.

  3. Lors de l'insertion de plusieurs vues supplémentaires, les appels faits par la vue de la collection pour les attributs de mise en page initiaux et finaux semblent être déséquilibrés. En d'autres termes, les attributs de mise en page initiaux sont demandés plusieurs fois pour le même index path (et, naturellement, les mêmes attributs sont renvoyés à chaque fois), et il y a moins de demandes pour les attributs de mise en page finaux. De plus, pour certaines vues supplémentaires existantes, les attributs de mise en page initiaux et finaux ne sont jamais demandés du tout. Je trouve cela suspect. Cela me fait penser que la vue de la collection confond de quelque manière les vues supplémentaires et/ou les index paths avant et après la mise à jour.

  4. Les adresses mémoire des instances d'IndexPath créées par mon code Swift sont très différentes des instances de NSIndexPath créées internalement par UICollectionView et UICollectionViewFlowLayout. Les premières sont toujours différentes (comme prévu), tandis que les secondes semblent être identiques entre les lancements (par exemple [0, 0], [0, 1] et [0, 2] sont toujours à 0xc000000000000016, 0xc000000000200016 et 0xc000000000400016). En Swift, IndexPath est converti en NSIndexPath, mais le premier est un type de valeur tandis que le second est un type de référence. NSIndexPath utilise également des pointeurs taggés. Il y a une possibilité lointaine que les deux aient été imparfaitement convertis et/ou que les classes de la vue de la collection reposent en interne sur le comportement de NSIndexPath d'une manière qui donne lieu à la confusion des index paths apparente ici. Je ne sais pas comment tester cela ou aller plus loin.

Questions similaires

Les questions suivantes peuvent être liées, bien que aucune des réponses ne fonctionne pour moi:

Le problème peut également être lié à ce rapport de bug sur Open Radar. Cependant, le projet exemple accompagnant ce rapport est un peu trop compliqué pour être utile.

Support technique Apple

Après avoir publié cette question, j'ai soumis un incident de support technique à Apple. Le Support aux développeurs d'Apple a répondu ce qui suit:

Nos ingénieurs ont examiné votre demande et ont déterminé qu'il serait préférable de traiter cela comme un rapport de bug.

Veuillez soumettre un rapport de bug complet concernant ce problème en utilisant l'outil de signalement de bug disponible à l'adresse https://developer.apple.com/bug-reporting/.

...

Nous avons passé du temps à étudier la possibilité d'une solution de contournement et malheureusement nous sommes revenus bredouilles. Veuillez suivre avec votre rapport de bug. Si nous découvrons la possibilité d'une sorte de solution de contournement à l'avenir, nous vous contacterons. Désolé de ne pas avoir de meilleures nouvelles.

Si vous rencontrez ce problème, veuillez dupliquer le rdar://30510010.

2voto

dahiya_boy Points 5065

Voici la sortie du fichier CustomCollectionViewController indexPath item Row est pour cellForItemAt indexPath et Row Supplementary est pour viewForSupplementaryElementOfKind.

enter image description here

Comme vous pouvez le voir, cellForItemAt renvoie correctement le indexpath.item mais dans le viewForSupplementaryElementOfKind ne renvoie pas la réponse attendue. Je n'ai pas encore compris la raison, si je la trouve je vous tiendrai informé bientôt.

Donc pour résoudre le problème, vous pouvez traiter le tableau en ajoutant de nouvelles données et recharger la collectionView au lieu de l'indexpath.

2voto

Je ne suis pas sûr d'avoir une solution correcte pour le problème initial, mais j'avais le même problème que dans la réponse de @agent_stack.

Si vous avez une mise en page personnalisée de la vue de collection (sous-classe de UICollectionViewLayout ou sous-classe de UICollectionViewFlowLayout) et que vous ajoutez votre propre vue supplémentaire, vous devez remplacer une méthode supplémentaire : indexPathsToInsertForSupplementaryView(ofKind elementKind: String)

La vue de collection appelle cette méthode chaque fois que vous ajoutez des cellules ou des sections à la vue de collection. En implémentant cette méthode, votre objet de mise en page a l'opportunité d'ajouter de nouvelles vues supplémentaires pour compléter les ajouts.

J'ai fait ma propre CollectionViewFlowLayout et ce n'est qu'après avoir ajouté ces méthodes en combinaison que tout s'anime comme il se doit !

    override func prepare(forCollectionViewUpdates updateItems: [UICollectionViewUpdateItem]) {
    super.prepare(forCollectionViewUpdates: updateItems)

    // Préparez la mise à jour en stockant l'index du chemin d'accès à insérer
    // ────────────────────────────────────────────────────────────

    // IMPORTANT : Mise à jour de l'élément par rapport à la section
    //
    // Une instance de UICollectionViewUpdateItem peut représenter une mise à jour de l'élément ou de la section,
    // et la seule façon de savoir laquelle est que les éléments de mise à jour qui représentent des mises à jour de la section contiennent
    // des index de chemin d'accès avec un index de section valide et NSNotFound pour l'index de l'élément, alors que les éléments de mise à jour
    // qui représentent des mises à jour de l'élément contiennent des index de chemin d'accès avec des index d'élément valides.
    //
    // Cela semble être complètement non documenté par Apple, mais c'est un comportement absolument cohérent.

    // https://www.raizlabs.com/blog/2013/10/animating_items_uicollectionview/

    var indexPaths = [IndexPath]()

    for updateItem in updateItems {

        switch updateItem.updateAction {

        case .insert:

            // S'il s'agit d'une mise à jour de la section, convertissez NSNotFound en zéro.
            // Dans notre cas, une mise à jour de la section a toujours uniquement le premier élément.

            if var indexPath = updateItem.indexPathAfterUpdate {
                if indexPath.item == NSNotFound { indexPath.item = 0 }
                indexPaths.append(indexPath)
            }

        default:
            break
        }
    }

    indexPathsToInsert = indexPaths
}

override func indexPathsToInsertForSupplementaryView(ofKind elementKind: String) -> [IndexPath] {

    // Ajouter des index des chemins d'accès supplémentaires
    // ────────────────────────────────────────────────────────────

    // La vue de collection appelle cette méthode chaque fois que vous ajoutez des cellules ou des sections à la vue de collection.
    // En implémentant cette méthode, votre objet de mise en page a l'opportunité d'ajouter de nouvelles vues supplémentaires
    // pour compléter les ajouts.

    // http://stackoverflow.com/a/38289843/7441991

    switch elementKind {

    case UICollectionElementKindDateSeparator:
        return indexPathsToInsert

    case UICollectionElementKindAvatar:
        return indexPathsToInsert

    default:
        // Si ce n'est pas une vue supplémentaire personnalisée, retournez les attributs de la mise en page de flux
        return super.indexPathsToInsertForSupplementaryView(ofKind: elementKind)
    }
}

N'hésitez pas à me contacter si vous avez des questions ! :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X