Ich unterteile UICollectionViewFlowLayout
, um in zwei Richtungen in einem UICollectionView
zu scrollen. Das Scrollen funktioniert gut für eine kleinere Anzahl von Zeilen- und Abschnittszählungen (100-200 Zeilen und Abschnitte), aber es gibt eine sichtbare Verzögerung beim Scrollen, wenn ich die Anzahl der Zeilen und Sektionen über 500 d. H. 250.000 oder mehr Zellen in UICollectionView
erhöhe. Ich habe die Quelle der Verzögerung für in Schleife in der layoutAttributesForElementsInRect
verfolgt. Ich bin mit einem Dictionary
UICollectionViewLayoutAttributes
jeder Zelle zu halten, es zu vermeiden, neu zu berechnen und Looping durch Attribute von Zellen zurückzukehren, von layoutAttributesForElementsInRect
Sichtbare Verzögerung beim Scrollen durch zweiseitiges Scrollen UICollectionView mit großer Anzahl von Zellen (250.000 oder mehr)
import UIKit
class LuckGameCollectionViewLayout: UICollectionViewFlowLayout {
// Used for calculating each cells CGRect on screen.
// CGRect will define the Origin and Size of the cell.
let CELL_HEIGHT = 70.0
let CELL_WIDTH = 70.0
// Dictionary to hold the UICollectionViewLayoutAttributes for
// each cell. The layout attribtues will define the cell's size
// and position (x, y, and z index). I have found this process
// to be one of the heavier parts of the layout. I recommend
// holding onto this data after it has been calculated in either
// a dictionary or data store of some kind for a smooth performance.
var cellAttrsDictionary = Dictionary<NSIndexPath, UICollectionViewLayoutAttributes>()
// Defines the size of the area the user can move around in
// within the collection view.
var contentSize = CGSize.zero
override func collectionViewContentSize() -> CGSize {
return self.contentSize
}
override func prepareLayout() {
// Cycle through each section of the data source.
if collectionView?.numberOfSections() > 0 {
for section in 0...collectionView!.numberOfSections()-1 {
// Cycle through each item in the section.
if collectionView?.numberOfItemsInSection(section) > 0 {
for item in 0...collectionView!.numberOfItemsInSection(section)-1 {
// Build the UICollectionVieLayoutAttributes for the cell.
let cellIndex = NSIndexPath(forItem: item, inSection: section)
let xPos = Double(item) * CELL_WIDTH
let yPos = Double(section) * CELL_HEIGHT
let cellAttributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: cellIndex)
cellAttributes.frame = CGRect(x: xPos, y: yPos, width: CELL_WIDTH, height: CELL_HEIGHT)
// Save the attributes.
cellAttrsDictionary[cellIndex] = cellAttributes
}
}
}
}
// Update content size.
let contentWidth = Double(collectionView!.numberOfItemsInSection(0)) * CELL_WIDTH
let contentHeight = Double(collectionView!.numberOfSections()) * CELL_HEIGHT
self.contentSize = CGSize(width: contentWidth, height: contentHeight)
}
override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
// Create an array to hold all elements found in our current view.
var attributesInRect = [UICollectionViewLayoutAttributes]()
// Check each element to see if it should be returned.
for (_,cellAttributes) in cellAttrsDictionary {
if CGRectIntersectsRect(rect, cellAttributes.frame) {
attributesInRect.append(cellAttributes)
}
}
// Return list of elements.
return attributesInRect
}
override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
return cellAttrsDictionary[indexPath]!
}
override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
return false
}
}
Edit: Im Anschluss an die Veränderungen, die ich gekommen bin, oben mit in dem layoutAttributesForElementsInRect
Methode.
override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
// Create an array to hold all elements found in our current view.
var attributesInRect = [UICollectionViewLayoutAttributes]()
let xOffSet = self.collectionView?.contentOffset.x
let yOffSet = self.collectionView?.contentOffset.y
let totalColumnCount = self.collectionView?.numberOfSections()
let totalRowCount = self.collectionView?.numberOfItemsInSection(0)
let startRow = Int(Double(xOffSet!)/CELL_WIDTH) - 10 //include 10 rows towards left
let endRow = Int(Double(xOffSet!)/CELL_WIDTH + Double(Utils.getScreenWidth())/CELL_WIDTH) + 10 //include 10 rows towards right
let startCol = Int(Double(yOffSet!)/CELL_HEIGHT) - 10 //include 10 rows towards top
let endCol = Int(Double(yOffSet!)/CELL_HEIGHT + Double(Utils.getScreenHeight())/CELL_HEIGHT) + 10 //include 10 rows towards bottom
for(var i = startRow ; i <= endRow; i = i + 1){
for (var j = startCol ; j <= endCol; j = j + 1){
if (i < 0 || i > (totalRowCount! - 1) || j < 0 || j > (totalColumnCount! - 1)){
continue
}
let indexPath: NSIndexPath = NSIndexPath(forRow: i, inSection: j)
attributesInRect.append(cellAttrsDictionary[indexPath]!)
}
}
// Return list of elements.
return attributesInRect
}
Ich habe den Offset des Collection und die verwendete berechnet, um die Zellen zu berechnen, die auf dem Bildschirm sichtbar sein werden (Höhe/Breite jeder Zelle verwendet wird). Ich musste auf jeder Seite zusätzliche Zellen hinzufügen, damit beim Scrollen keine Zellen fehlen. Ich habe das getestet und die Leistung ist in Ordnung.
Offensichtlich ist das Durchlaufen des Wörterbuchs der teure Teil. Ich frage mich, ob es eine Möglichkeit gibt, Ihr Wörterbuch zu verschlüsseln, damit Sie alles bekommen können, was sich mit einem Rect kreuzt, ohne 250k-Werte durchlaufen zu müssen. –
@KevinDiTraglia Ich dachte auch darüber nach, aber das Problem ist, dass 'layoutAttributesForElementsInRect' nicht unbedingt nur die sichtbaren Elemente zurückgibt. Aus dem Dokument (https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/CreatingCustomLayouts/CreatingCustomLayouts.html) "Basierend auf der aktuellen Bildlaufposition ruft die Auflistungsansicht dann Ihr LayoutAttributesForElementsInRect auf: Methode, um nach den Attributen der Zellen und Ansichten in einem bestimmten Rechteck zu fragen, das mit dem sichtbaren Rechteck identisch sein kann oder nicht. " –