Back to Home

UICollectionView or dancing with wolves

The dream UICollectionView is the UIKit class introduced in iOS 6. Strictly speaking · this is a class that allows you to display a collection of items on the screen. The structure of the collection is completely arbitrary · but usually ...

UICollectionView or dancing with wolves

    The dream


    UICollectionView is the UIKit class introduced in iOS 6. Strictly speaking, this is a class that allows you to display a collection of items on the screen. The structure of the collection is completely arbitrary, but usually the UICollectionView is used for all sorts of grid-like controls with cells, headers and footers. Realizing how abstract this class is, Apple developers have created a powerful mechanism for creating any layouts. By and large, even UITableView is a concrete implementation of UICollectionView. The capabilities of this class are, in a sense, fantastic. But this article is not about that.

    The Achilles Heel of Apple Developers is a constant desire to make KFOR, which will work “automatically”. Just do this and that and the class will be the right thing. Unfortunately, this does not always work. And UICollectionView is a prime example. Starting from the release in iOS 6 to the present day (iOS 7.0.4), the class contains a rather large number of bugs, which are very difficult and unpleasant to deal with. We have to guess what is happening “under the hood”, and use the poke method to make UICollectionView work as it should. The number of crutches I purchased has already reached such proportions that I decided to share the known bugs and solutions found.

    Who cares - you are welcome under cat.

    Reality


    Before starting to describe bugs, I will try to describe a “perfect scenario” in which the UICollectionView works stably, without any crutches. Typically, this is a UICollectionView, in which there are no headers, and footers, only cells. When the controller has loaded into memory (viewDidLoad :), the UICollectionView -reloadData method is called. Animated deletions, moves, and inserts are not done. Here is such an ideal scenario for a UICollectionView without crutches. As you can see, it is quite limited.

    Immediately make a reservation that you may not encounter bugs from the list, which I will give below. Each bug was in specific conditions that you may not have. And each solution also helped in my particular case, there is no guarantee that it will help you, and that it will continue to work in future iOS releases. Many of the crutches will look ugly, but which crutch is not ugly? However, if something helps, I will be happy =). Go!

    iOS 6 + iOS 7


    1. You cannot insert the first UICollectionViewCell into sections. Also, you cannot delete the last remaining UICollectionViewCell. Attempting to do this leads to crash in debug builds, and unpredictable behavior in release builds:

    *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'request for index path for global index 1 when there are only 1 items in the collection view'

    Comment. The most famous bug with UICollectionView. Open radar on Apple bug tracker. The most popular and stable solution is to call reloadData. Sometimes it can help to call the insertItemsAtIndexPaths: method in a try-catch block (yes, the leg was ruthlessly shot).

    @try {
        [self.collectionView insertItemsAtIndexPaths:indexPaths];
    }
    @catch (NSException *exception) {}
    

    Another solution, which is probably not worth using, but worth mentioning, is to call reloadData inside the performBatchUpdates block.

    [self.collectionView performBatchUpdates:^{
            [self.collectionView reloadData];
        } completion:nil];
    

    2. Attempting to perform two animation operations simultaneously without performBatchUpdates block will cause the application to crash.

    *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of sections. The number of sections contained in the collection view after the update (1) must be equal to the number of sections contained in the collection view before the update (0), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted). '

    Comment. This paragraph may seem insignificant, but in fact it is very important. 80% of my personal problems with UICollectionView arose when it was necessary to group several animations. In particular, this is due to NSFetchedResultsController and updates to the CoreData database. There is an open source solution from Ash Furrow that allows you to work correctly with updates in CoreData.

    3. You cannot update sections and cells in one performBatchUpdates block. Upgrade refers to insertion or deletion.

    *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of sections. The number of sections contained in the collection view after the update (1) must be equal to the number of sections contained in the collection view before the update (0), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted). '

    Comment. A rather strange problem, two consecutive performBatchUpdates blocks help, in the first block sections are updated, in the second block cells.

    4. You cannot call performBatchUpdates immediately after calling reloadData.

    attempt to delete section 0, but there are only 0 sections before the update

    Comment. It seems that reloadData in UICollectionView works asynchronously, unlike UITableView.

    Update as of February 5, 2014. There is a crutch method to call reloadData synchronously. To do this, create a new UICollectionViewLayout object and call the method

    [collectionView setCollectionViewLayout:layout animated:NO];
    

    The method inside will call the private synchronous reloadData. The solution works ONLY if there were already elements / cells in the UICollectionView. If there were no cells - for the first time nothing will happen, the second time you will crash. Thanks to the user PALKOVNIK for the hack suggested.

    Update as of March 3, 2014. The second crutch way to update the UICollectionView synchronously:

    [collection.collectionView reloadData];
    [collection.collectionView performBatchUpdates:nil completion:nil];
    


    5. When using UICollectionViewFlowLayout, if headerSize or footerSize is nonzero, the collectionView: viewForSupplementaryElementOfKind: atIndexPath: method must not return nil, otherwise it will crash.

    *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'the view returned from -collectionView: viewForSupplementaryElementOfKind: atIndexPath (UICollectionElementKindSectionFooter, <NSIndexPath: 0x8e55df0> {length = 2} by not = 0 - retrieved calling -dequeueReusableSupplementaryViewOfKind: withReuseIdentifier: forIndexPath: or is nil ((null)) '

    Comment. If you have a UICollectionView with no header or footer, you must define a delegate method

     -(CGSize)collectionView:(UICollectionView *)collectionView
                     layout:(UICollectionViewFlowLayout *)collectionViewLayout
    referenceSizeForHeaderInSection:(NSInteger)sectionNumber
    

    And if you have no header in this section, return CGSizeZero.

    iOS 6


    1. UICollectionViewFLowLayout. If you have a section without cells, and you have not defined the delegate method collectionView: layout: referenceSizeForHeaderInSection :, you are out of luck, crash runtime.

    *** Assertion failure in - [UICollectionViewData indexPathForItemAtGlobalIndex:]
    request for index path for global index 805306368 when there are only 0 items in the collection view

    Comment. You must define the collectionView: layout: referenceSizeForHeaderInSection: method, and if there are no cells in the section, return CGSizeZero.

    2. Sometimes after calling reloadData, the old cells remain in place, despite the fact that the UICollectionViewDatasource methods clearly return that they are not. It is especially common when the UICollectionView is closed by the keyboard, or by another UIView. Unfortunately, there is no solution.

    iOS 7


    1. If the cell is the last in the section, as well as the presence of footers in the UICollectionView, calling the moveItemAtIndexPath: toIndexPath: method for this cell in another section will lead to a fall

    Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'no UICollectionViewLayoutAttributes instance for -layoutAttributesForSupplementaryElementOfKind: UICollectionElementKindSectionFooter at path <NSIndexPath: 0xc054730> = length = 2,

    Comment. This problem is very similar to inserting and removing the first item in the section, so the solution is the same, we call reloadData if the item is the last.

    2. Deleting a section at a time when the UICollectionView is not displayed on the screen leads to a fall.

    *** Assertion failure in - [UICollectionViewData validateLayoutInRect:], /SourceCache/UIKit_Sim/UIKit-2903.23/UICollectionViewData.mhaps41
    2014-01-10 17: 34: 55.198 SMS-Bank [47090: 70b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UICollectionView recieved layout attributes for a cell with an index path that does not exist: <NSIndexPath: 0xc000000000000056> {length = 2, path = 1 - 0}'

    Comment . At that moment when the UICollectionView will disappear, we nullify the delegate and datasource.

    self.collectionView.delegate = nil;
    self.collectionView.dataSource = nil;
    


    3. iOS simulator, iPad 2 and iPad 3 do not use reuse for UICollectionViewCell. Also, a rebuild may fail if Accessibility shortcuts are enabled.

    Comment.

    You can read more on the stack: http://stackoverflow.com/questions/19276509/uicollectionview-do-not-reuse-cells
    Radar: http://openradar.appspot.com/15357491

    The end


    The list may be far from complete, unfortunately the UICollectionView is rather unstable. I hope someday Apple developers bring it to mind. In the meantime, I can recommend a library that was created for convenient work with UICollectionView.

    Actually, it was in the process of writing this library that the author found most of the bugs described in the article. The goal of this framework has never been to fix iOS SDK bugs, however today crutches added for iOS 6 and iOS 7 have become one of the important features. The ability to forget about the nightmare called UICollectionView + NSInternalInconsistencyException is priceless. If you have alternative solutions, or information about other UICollectionView bugs - share it in the comments!

    Thanks for attention!

    Read Next