Sono sottoclasse UICollectionViewFlowLayout
per ottenere lo scorrimento a due direzioni in un UICollectionView
. Lo scorrimento funziona bene per un numero minore di righe e sezioni (100-200 righe e sezioni), ma è visibile un ritardo durante lo scorrimento quando aumento il numero di righe e sezioni oltre 500 i.e 250.000 o più celle nello UICollectionView
. Ho rintracciato l'origine del lag da inserire nel ciclo nello layoutAttributesForElementsInRect
. Sto usando un Dictionary
per tenere UICollectionViewLayoutAttributes
di ogni cella per evitare di ricalcolare e loop attraverso di essa per restituire gli attributi di cellule da layoutAttributesForElementsInRect
Ritardo visibile durante lo scorrimento in due direzioni di scorrimento di UICollectionView con un numero elevato di celle (250.000 o più)
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: Di seguito sono riportati i cambiamenti che sono venuto su con la Metodo layoutAttributesForElementsInRect
.
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
}
Ho calcolato l'offset del CollectionView e utilizzati per calcolare le cellule che saranno visibili sullo schermo (usando altezza/larghezza di ciascuna cella). Ho dovuto aggiungere celle aggiuntive su ciascun lato in modo che quando l'utente scorre non ci siano celle mancanti. Ho provato questo e le prestazioni vanno bene.
Ovviamente il ciclo attraverso il dizionario è la parte costosa. Mi chiedo se è possibile digitare il dizionario in modo da poter ottenere tutto ciò che si interseca con un rect senza dover eseguire il ciclo di valori di 250k. –
@KevinDiTraglia Ho pensato anche a questo, ma il problema è che 'layoutAttributesForElementsInRect' non restituisce necessariamente solo gli elementi visibili. Dal doc (https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/CreatingCustomLayouts/CreatingCustomLayouts.html) 'In base alla posizione di scorrimento corrente, la vista raccolta chiama quindi il layoutAttributesForElementsInRect: metodo per chiedere gli attributi delle celle e delle viste in un rettangolo specifico, che può o non può essere lo stesso del rettangolo visibile. –