UICollectionView reloadData does not reload when preceded by performBatchUpdates

Originator:smiller
Number:rdar://31748196 Date Originated:20-Apr-2017 05:27 PM
Status:Open Resolved:
Product:iOS + SDK Product Version:iOS 10.2, iOS 10.3
Classification:UIKit Reproducible:Always
 
Area:
UIKit

Summary:
When calling `UICollectionView.performBatchUpdates` then soon after calling `UICollectionView.reloadData`, the `UICollectionView` is not properly reloaded. In this bad case, `UICollectionViewDataSource.collectionView(_:cellForItemAt:)` is never called for the `reloadData`.

This issue is not experience if `UICollectionView.reloadSections` is used in place of `reloadData`.

Here are some example method traces from the demo project:

Here we have `performBatchUpdates` followed by `reloadData` and we can see that no call is made to `UICollectionViewDataSource.collectionView(_:cellForItemAt:)` as expected with `reloadData`.
```
2017-04-20 16:39:25.944 CollectionViewBug[78449:18671781] CollectionView.performBatchUpdates
2017-04-20 16:39:25.945 CollectionViewBug[78449:18671781] ViewController.performBatchUpdates update closure
2017-04-20 16:39:25.946 CollectionViewBug[78449:18671781] ViewController.collectionView(_:layout:sizeForItemAt: 0-0)
2017-04-20 16:39:25.946 CollectionViewBug[78449:18671781] ViewController.collectionView(_:layout:sizeForItemAt: 0-1)
2017-04-20 16:39:25.947 CollectionViewBug[78449:18671781] ViewController.collectionView(_:layout:sizeForItemAt: 0-2)
2017-04-20 16:39:25.947 CollectionViewBug[78449:18671781] ViewController.collectionView(_:layout:sizeForItemAt: 0-3)
2017-04-20 16:39:25.948 CollectionViewBug[78449:18671781] ViewController.collectionView(_:layout:sizeForItemAt: 0-4)
2017-04-20 16:39:25.948 CollectionViewBug[78449:18671781] ViewController.collectionView(_:layout:sizeForItemAt: 0-5)
2017-04-20 16:39:25.949 CollectionViewBug[78449:18671781] ViewController.collectionView(_:layout:sizeForItemAt: 0-6)
2017-04-20 16:39:25.950 CollectionViewBug[78449:18671781] CollectionView.reloadData
2017-04-20 16:39:25.951 CollectionViewBug[78449:18671781] ViewController.collectionView(_:layout:sizeForItemAt: 0-0)
2017-04-20 16:39:26.000 CollectionViewBug[78449:18671781] ViewController.collectionView(_:layout:sizeForItemAt: 0-1)
2017-04-20 16:39:26.000 CollectionViewBug[78449:18671781] ViewController.collectionView(_:layout:sizeForItemAt: 0-2)
2017-04-20 16:39:26.000 CollectionViewBug[78449:18671781] ViewController.collectionView(_:layout:sizeForItemAt: 0-3)
2017-04-20 16:39:26.001 CollectionViewBug[78449:18671781] ViewController.collectionView(_:layout:sizeForItemAt: 0-4)
2017-04-20 16:39:26.001 CollectionViewBug[78449:18671781] ViewController.collectionView(_:layout:sizeForItemAt: 0-5)
2017-04-20 16:39:26.001 CollectionViewBug[78449:18671781] ViewController.collectionView(_:layout:sizeForItemAt: 0-6)
2017-04-20 16:39:26.001 CollectionViewBug[78449:18671781] ViewController.performBatchUpdates complete closure
2017-04-20 16:39:26.002 CollectionViewBug[78449:18671781] CollectionView.layoutSubviews
```

Here we have `performBatchUpdates` followed by `reloadSections` and we can see that calls are properly made to `UICollectionViewDataSource.collectionView(_:cellForItemAt:)` as expected.
```
2017-04-20 17:13:28.620 CollectionViewBug[79697:18821731] CollectionView.performBatchUpdates
2017-04-20 17:13:28.621 CollectionViewBug[79697:18821731] ViewController.performBatchUpdates update closure
2017-04-20 17:13:28.622 CollectionViewBug[79697:18821731] ViewController.collectionView(_:layout:sizeForItemAt: 0-0)
2017-04-20 17:13:28.622 CollectionViewBug[79697:18821731] ViewController.collectionView(_:layout:sizeForItemAt: 0-1)
2017-04-20 17:13:28.622 CollectionViewBug[79697:18821731] ViewController.collectionView(_:layout:sizeForItemAt: 0-2)
2017-04-20 17:13:28.623 CollectionViewBug[79697:18821731] ViewController.collectionView(_:layout:sizeForItemAt: 0-3)
2017-04-20 17:13:28.623 CollectionViewBug[79697:18821731] ViewController.collectionView(_:layout:sizeForItemAt: 0-4)
2017-04-20 17:13:28.623 CollectionViewBug[79697:18821731] ViewController.collectionView(_:layout:sizeForItemAt: 0-5)
2017-04-20 17:13:28.624 CollectionViewBug[79697:18821731] ViewController.collectionView(_:layout:sizeForItemAt: 0-6)
2017-04-20 17:13:28.625 CollectionViewBug[79697:18821731] CollectionView.reloadSections
2017-04-20 17:13:28.625 CollectionViewBug[79697:18821731] ViewController.collectionView(_:layout:sizeForItemAt: 0-0)
2017-04-20 17:13:28.625 CollectionViewBug[79697:18821731] ViewController.collectionView(_:layout:sizeForItemAt: 0-1)
2017-04-20 17:13:28.626 CollectionViewBug[79697:18821731] ViewController.collectionView(_:layout:sizeForItemAt: 0-2)
2017-04-20 17:13:28.626 CollectionViewBug[79697:18821731] ViewController.collectionView(_:layout:sizeForItemAt: 0-3)
2017-04-20 17:13:28.626 CollectionViewBug[79697:18821731] ViewController.collectionView(_:layout:sizeForItemAt: 0-4)
2017-04-20 17:13:28.626 CollectionViewBug[79697:18821731] ViewController.collectionView(_:layout:sizeForItemAt: 0-5)
2017-04-20 17:13:28.627 CollectionViewBug[79697:18821731] ViewController.collectionView(_:layout:sizeForItemAt: 0-6)
2017-04-20 17:13:28.627 CollectionViewBug[79697:18821731] ViewController.collectionView(_:cellForItemAt: 0-0)
2017-04-20 17:13:28.629 CollectionViewBug[79697:18821731] ViewController.collectionView(_:cellForItemAt: 0-1)
2017-04-20 17:13:28.629 CollectionViewBug[79697:18821731] ViewController.collectionView(_:cellForItemAt: 0-2)
2017-04-20 17:13:28.630 CollectionViewBug[79697:18821731] ViewController.collectionView(_:cellForItemAt: 0-3)
2017-04-20 17:13:28.631 CollectionViewBug[79697:18821731] CollectionView.layoutSubviews
2017-04-20 17:13:28.633 CollectionViewBug[79697:18821731] ViewController.performBatchUpdates complete closure
2017-04-20 17:13:28.633 CollectionViewBug[79697:18821731] CollectionView.layoutSubviews
2017-04-20 17:13:29.002 CollectionViewBug[79697:18821731] CollectionView.layoutSubviews
```

Steps to Reproduce:
1. Call `UICollectionView.performBatchUpdates(nil, completion: nil)`
2. Optionally, update the UICollectionViewDataSource (this will help visualize the issue)
3. Call `UICollectionView.reloadData()` immediately afterward (all of this in the same method is the easiest)

Expected Results:
All cells are reloaded by calling `UICollectionViewDataSource.collectionView(_:cellForItemAt:)`.  

Actual Results:
`UICollectionViewDataSource.collectionView(_:cellForItemAt:)` is never called.

Version:
iOS 10.2.1 (14D27), iOS Simulator 10.2 (14C89), Xcode Version 8.2.1 (8C1002), iOS Simulator 10.3 (14E269), Xcode Version 8.3.2 (8E2002)

Notes:
For using the included demo project:

There are some notes at the top of ViewController.swift, the main file.

1. Run the project on any device or simulator.
2. Tap the button labeled "Bad"
  - You should see that the cells change size but do not reload (they should reload to yellow). If you scroll around you will see that the cells do correctly configure when scrolled on to the screen.
3. Tap the button labeled "Reset"
  - This will reset the example to its original state
4. Tap the button labeled "Good"
  - You should see the proper reloading behavior since this uses `reloadSections`
5. Repeat 2-4 after changing `DISABLE_ANIMATIONS` at the top of the ViewController.swift file to `true`. This just demonstrates the same issue occurs when not using animations for the animated UICollectionView methods.

Configuration:
iPhone 7 Plus 128G AT&T, iPhone Simulator

Attachments:
'CollectionViewBug.zip' was successfully uploaded.

Comments


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!