UICollectionView with custom layout
I've created a styling app for styling for anyone using a UICollectionView with a custom UICollectionViewLayout. The custom layout uses four different custom UICollectionViewCells. I ran into a very interesting problem that prompted me and my team. My post is quite long, but it's done in a way that hopefully gives you the full context. If you have time to read and throw me your ideas. I would greatly appreciate it. As always thanks to the SO community for all your help.
tl; dr - A UICollectionView with a custom layout (subclass of UICollectionViewLayout) randomly loses all data in the view. It passes completely. .reloadData()
is called on viewDidAppear
and is not committed. Let's dwell on the reason.
Section 0, item 0 is one type of cell and is always found in the top left corner. As you scroll, the cell moves with you and is stored in the upper left corner.
Section 0, item 1-n are the second type of cell and are always stored at the top.
Section 1-n, Item 0-n are "stations", another type of cell, and are always stored along the left border.
All other cells are "metrics" and the fourth type of cell and are mapped to a specific x, y coordinate based on time and station.
This collection is very effective and has enabled us to provide a great user experience for the most part. However, with a full load of data in 10 days. It uses a decent amount of memory. ~ 100-150MB depending on the composition, the number of stations and the duration of the show. Shorter displays mean more cells are displayed as a collection.
When the app is working with a lot of manual data, sometimes the CollectionView will hiccup and my floating headers will stop moving. Then ALL of my cells will disappear. We have fondly started calling this white screen of death as the entire collection is displayed empty. At this time, the scrollbars are still visible and you can see that you are scrolling, but no cells are loaded.
We created an issue multiple times with a device connected to the Xcode debugger and having some data. The data to fill all the cells is still available from the debugger. cellForItemAtIndexPath
can be called from the debugger and returns a UICollectionViewCell. numberOfSections
and numberOfItemsInSections
still function and display the correct numbers when printed from the debugger. The function is cellForItemAtIndexPath
never called after the collection is empty. The breakpoint in this method will never be deleted. The layout is still available and the variables are available without issue.
Now for things that seem abnormal, but we don't know what to do with them.
collectionView.contentSize
and are collectionView.collectionViewLayout.contentSize
different. On a non-interrupt device, they are the same when viewing the debugger.
collectionView.visibleCells
returns an empty array. This will be technically correct since our collection is in the white screen of death mode ... but we have sections and items in sections, so not sure about that.
The problem is intermittent, but we can usually play several times per hour if we try hard enough. We think it has to do with memory because it never happens on a simulator. On physical devices only.
Since this is an unreleased client app, I've clouded their navigation controls.
Has anyone seen a similar problem? Does anyone have any ideas for what to try next?
One final note, THANKS FOR READING MORE! :)
source to share
Seeing someone just backing the question up, I thought I'd go back and post the permission we landed on.
Our collectionView was calling one single that managed a lot of data for the app. The singleton filled up in the background as users scrolled to save the data in the view. These background updates for the singleton were controlled by background threads running at low priority. We first created streams to update the datastores and then updated the accessor methods to learn about the new data. This was our attempt at thread safety, since the accessory didn't know there was any other data other than what already existed until the data was changed. However, it didn't work. See below for details.
After extensive testing with multiple devices, we realized that our problem manifested itself in a very high volume on certain days, we took this data and started analyzing the cells of the collectionView for the date data, we came to the conclusion that on the days when the problem occurred regularly there were cells were created that were very large, for example, one cell in a row could span 4-5 iPhone widths. After some searching, we found that this was a common problem that people faced and most of them just shrunk their cells. However, our cells are associated with time and length.
More research was put forward and we eventually took a swing while removing all threads. This meant that when our user got to the end of the collection, we would nudge a modal view that would show an activity indicator and block user input while the singleton was updated. This solved our problem IMMEDIATELY.
Notes from our research:
- Extremely large cells, width or height, cause problems for the collectionView and take extra care.
- These large cells can cause problems at a much higher frequency than usual, however, they do not appear to be the cause of our problem.
- Thread safety was our concern, at some point the collectionView lost contact with the data source and it could not recover. We tried several tools to redirect the data source, none worked. Fixed deletion of all streams.
Hope this helps!
source to share