gpt4 book ai didi

ios - 滚动具有大量单元格(250,000 或更多)的两种方式滚动 UICollectionView 时可见滞后

转载 作者:IT王子 更新时间:2023-10-29 05:51:34 28 4
gpt4 key购买 nike

我正在对 UICollectionViewFlowLayout 进行子类化,以便在 UICollectionView 中实现双向滚动。对于较少数量的行和部分计数(100-200 行和部分),滚动效果很好,但是当我将行和部分计数增加超过 500,即 UICollectionView 中的 250,000 或更多单元格时,滚动时会出现明显的滞后>。我已经在 layoutAttributesForElementsInRect 中追踪了延迟的来源是 for in 循环。我正在使用 Dictionary 来保存每个单元格的 UICollectionViewLayoutAttributes 以避免重新计算它并循环遍历它以从 layoutAttributesForElementsInRect

返回单元格的属性>
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
}
}

编辑:以下是我在 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
}

我已经计算了 collectionView 的偏移量并使用它来计算将在屏幕上可见的单元格(使用每个单元格的高度/宽度)。我不得不在每一侧添加额外的单元格,这样当用户滚动时就不会丢失单元格。我已经对此进行了测试,性能很好。

最佳答案

通过利用具有已知单元格大小的 layoutAttributesForElementsInRect(rect: CGRect),您无需缓存属性,只需为给定的 rect 计算它们> 当 collectionView 请求它们时。您仍然需要检查 0 的边界情况和最大节数/行数以避免计算不需要或无效的属性,但这可以在循环周围的 where 子句中轻松完成。这是一个工作示例,我已经用 1000 个部分 x 1000 行进行了测试,它工作得很好,在设备上没有滞后:

编辑:我添加了 biggerRect 以便在滚动到达之前可以预先计算属性。从您的编辑来看,您似乎仍在缓存我认为性能不需要的属性。此外,滚动次数越多,内存占用量也会越大。还有一个原因是您不想使用回调中提供的 CGRect 而不是手动从 contentOffset 中计算一个吗?

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

关于ios - 滚动具有大量单元格(250,000 或更多)的两种方式滚动 UICollectionView 时可见滞后,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37520764/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com