8
votes

Décalage visible lors du défilement de deux voies de défilement uicollectionview avec un grand nombre de cellules (250 000 ou plus)

Je suis sous-classement uicollectionviewflowlayout afin de faire défiler deux choses dans un uicollectionview . Le défilement fonctionne bien pour un nombre réduit de comptage de rangée et de section (100-200 rangées et sections), mais il existe un décalage visible lors du défilement lorsque j'augmente la ligne et la section comptent sur 500 c'est-à-dire 250 000 ou plus de cellules dans le uicollectionview . J'ai tracé la source du décalage pour être en boucle dans le LayoutaTtributesforelementsinrect . J'utilise un dictionnaire pour enfoncé uicollectionviewlayouttattributes de chaque cellule pour éviter de la recalculer et de boucler à travers elle pour renvoyer les attributs des cellules de Layoutetattributesforelementsinrect Xxx

EDIT: Voici les changements que j'ai proposés dans la méthode LayoutaTtributesforelementsinrect . xxx

J'ai calculé le décalage de la collectionView et l'a utilisé pour calculer Les cellules qui seront visibles à l'écran (à l'aide de la hauteur / de la largeur de chaque cellule). Je devais ajouter des cellules supplémentaires de chaque côté afin que lorsque l'utilisateur défile, il n'y a pas de cellules manquantes. J'ai testé cela et la performance va bien.


2 commentaires

Évidemment, la boucle à travers le dictionnaire est la partie chère. Je me demande s'il y a quelque part pour clés de votre dictionnaire afin que vous puissiez obtenir tout ce qui se croisit avec un record sans avoir besoin de boucler à travers 250 000 valeurs.


@Kevinditraglia J'ai réfléchi à cela aussi, mais le problème est que Layoutetattributesforelementsinrect ne renvoie pas nécessairement que les éléments visibles. De la doc ( développeur.apple.com/ Bibliothèque / iOS / Documentation / Windowsviews / ... ) Basé sur la position de défilement actuelle, la vue Collection appelle ensuite votre layouTatributesForelementsInrect: méthode pour demander les attributs des cellules et des vues dans un rectangle spécifique, qui peut être ou non le même que le rectangle visible.


3 Réponses :


1
votes

Vous devez trouver une structure de données pour vos cellules commandée par dimension (s), afin de proposer un algorithme à l'aide de ces dimensions (s) pour réduire la plage de recherche.

Prenons le cas d'une table avec des cellules 100 pixels hauts par la largeur complète de la vue et 250_000 éléments, demandées aux cellules intersectant {0, haut, 320, bas}. Ensuite, votre structure de données serait une matrice commandée par la coordonnée supérieure et l'algorithme d'accompagnement serait comme xxx

ajoutez autant de complexité que votre mise en page réelle nécessite. < / p>


0 commentaires

1
votes

cellattrsdictionner code> est inapproprié de stocker le uicollectionviewlayouttattributes code>. Il est indexé par nsindexpath code> mais dans Layoutetattributesforelementsinrect code> Vous recherchez une zone. Si vous avez besoin cellattrsdictionner code> pour autre chose, vous pouvez le laisser mais stocker chaque cellattribute en outre forte> dans une autre structure de données plus rapide à rechercher.

Par exemple, un tableau imbriqué: P>

allCellAttributes[5]
allCellAttributes[6]


0 commentaires

4
votes

En tirant parti des LayoutaTtributesForelementsIrect (rect: CGRRECT) CODE> Avec votre taille de cellule connue, vous n'avez pas besoin de cacher vos attributs et de les calculer pour un rect code > Comme la collectionView les demande. Vous auriez toujours besoin de vérifier les cas de frontière de 0 et le nombre maximum de section / rangée pour éviter de calculer des attributs inutiles ou non valides, mais cela peut être facilement effectué dans où code> clauses autour des boucles. Voici un exemple de travail que j'ai testé avec 1000 sections x 1000 rangées et cela fonctionne simplement sans être à la traîne sur le périphérique:

Edit: J'ai ajouté le BiggerRect afin que les attributs puissent être pré-calculés avant avant le défilement là. De votre édition, on dirait que vous cachez toujours les attributs que je ne pense pas être nécessaire pour la performance. En outre, cela va conduire à une empreinte de mémoire beaucoup plus grande avec plus vous faites défiler. Y a-t-il également une raison pour laquelle vous ne voulez pas utiliser le CGRACT CODE> à partir du rappel plutôt que de calculer manuellement l'un de la contentoffset? P>

class LuckGameCollectionViewLayout: UICollectionViewFlowLayout {

let CELL_HEIGHT = 50.0
let CELL_WIDTH = 50.0

override func collectionViewContentSize() -> CGSize {
    let contentWidth = Double(collectionView!.numberOfItemsInSection(0)) * CELL_WIDTH
    let contentHeight = Double(collectionView!.numberOfSections()) * CELL_HEIGHT
    return CGSize(width: contentWidth, height: contentHeight)
}

override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    let biggerRect = rect.insetBy(dx: -2048, dy: -2048)
    let startIndexY = Int(Double(biggerRect.origin.y) / CELL_HEIGHT)
    let startIndexX = Int(Double(biggerRect.origin.x) / CELL_WIDTH)
    let numberOfVisibleCellsInRectY = Int(Double(biggerRect.height) / CELL_HEIGHT) + startIndexY
    let numberOfVisibleCellsInRectX = Int(Double(biggerRect.width) / CELL_WIDTH) + startIndexX
    var attributes: [UICollectionViewLayoutAttributes] = []

    for section in startIndexY..<numberOfVisibleCellsInRectY
        where section >= 0 && section < self.collectionView!.numberOfSections() {
            for item in startIndexX..<numberOfVisibleCellsInRectX
                where item >= 0 && item < self.collectionView!.numberOfItemsInSection(section) {
                    let cellIndex = NSIndexPath(forItem: item, inSection: section)
                    if let attrs = self.layoutAttributesForItemAtIndexPath(cellIndex) {
                        attributes.append(attrs)
                    }
            }
    }
    return attributes
}

override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
    let xPos = Double(indexPath.row) * CELL_WIDTH
    let yPos = Double(indexPath.section) * CELL_HEIGHT
    let cellAttributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)
    cellAttributes.frame = CGRect(x: xPos, y: yPos, width: CELL_WIDTH, height: CELL_HEIGHT)
    return cellAttributes
}
}


3 commentaires

Pouvez-vous vérifier ma mise à jour avec la solution que j'ai proposée et des commentaires à ce sujet.


Toute suggestion sur l'obtention de cela pour travailler pour une largeur de cellule différente? Merci!


@Unikorn Si vous voulez avoir différentes largeurs de cellules pour chaque cellule, il est certainement plus difficile. Vous pouvez éventuellement enregistrer les attributs dans le dictionnaire comme dans l'exemple original de la question ci-dessus, mais cela augmenterait l'empreinte mémoire, ou stockerait le décalage X pour chaque section (ligne dans ce cas) qui incrèterait à mesure que l'utilisateur traîne vers la droite ou des décréments lors du glissement vers la gauche et est calculé en fonction de la largeur totale des cellules visibles plus l'espacement des cellules. Je suis sûr qu'il y aurait des problèmes avec cela qui auraient besoin d'explorer plus loin, mais cela pourrait être un début.