We are trying to customize a UICollectionView using a custom layout. The contents of each CollectionViewCell will be an image. For all this there will be several thousand images and about 140-150 visible at a certain time. In the event of an action, potentially all cells will be reorganized into position and size. The goal is to animate all moving events currently using the performBatchUpdates method. This leads to a huge delay time before everything becomes animated.
So far, we have found that internally the layoutAttributesForItemAtIndexPath method is called for each individual cell (only a few thousand). In addition, the cellForItemAtIndexPath method is called for more cells than can actually be displayed on the screen.
Are there any ways to improve animation performance?
By default, UICollectionViewFlowLayout cannot really offer the design that we want to implement in the application. Here are some of our code:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { RPDataModel *dm = [RPDataModel sharedInstance]; //Singleton holding some global information NSArray *plistArray = dm.plistArray; //Array containing the contents of the cells NSDictionary *dic = plistArray[[indexPath item]]; RPCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CELL" forIndexPath:indexPath]; cell.label.text = [NSString stringWithFormat:@"%@",dic[@"name"]]; cell.layer.borderColor = nil; cell.layer.borderWidth = 0.0f; [cell loadAndSetImageInBackgroundWithLocalFilePath:dic[@"path"]]; //custom method realizing asynchronous loading of the image inside of each cell return cell; }
LayoutAttributesForElementsInRect iterates through all the elements, setting the layoutAttributes for all the elements in the rectangle. The delay for the operator is divided into the first cell beyond the boundaries defined by the lower right corner of the rectangle:
-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect { NSMutableArray* attributes = [NSMutableArray array]; RPDataModel *dm = [RPDataModel sharedInstance]; for (int i = 0; i < dm.cellCount; i++) { CGRect cellRect = [self.rp getCell:i]; //self.rp = custom object offering methods to get information about cells; the getCell method returns the rect of a single cell if (CGRectIntersectsRect(rect, cellRect)) { NSIndexPath *indexPath = [NSIndexPath indexPathForItem:[dm.relevanceArray[i][@"product"] intValue] inSection:0]; UICollectionViewLayoutAttributes *attribute = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; attribute.size = cellRect.size; attribute.center = CGPointMake(cellRect.origin.x + attribute.size.width / 2, cellRect.origin.y + attribute.size.height / 2); [attributes addObject:attribute]; } else if (cellRect.origin.x > rect.origin.x + rect.size.width && cellRect.origin.y > rect.origin.y + rect.size.height) { break; } } return attributes; }
If you change the layout, the results are almost the same regardless of whether the number of cells defined in layoutAttributesForElementsInRect is limited or not. Either the system receives layout attributes for all cells, if they are not limited, or it calls the layoutAttributesForElementAtIndexPath method for all missing cells, if it is limited. In general, the attributes for each individual cell are used somehow.
-(UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath { RPDataModel *dm = [RPDataModel sharedInstance]; UICollectionViewLayoutAttributes *attribute = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; CGRect cellRect = [self.rp getCell:[dm.indexDictionary[@(indexPath.item)] intValue]]; attribute.size = cellRect.size; attribute.center = CGPointMake(cellRect.origin.x + attribute.size.width / 2, cellRect.origin.y + attribute.size.height / 2); return attribute; }