I have built a tv guide style application for someone that utilizes a UICollectionView with a custom UICollectionViewLayout. The custom layout uses four different custom UICollectionViewCells. I have run into a very interesting problem that has stumped me and my team. My post is quite long, but it is done that way to hopefully give you the full context. If you have the time to read and toss me your ideas. I would greatly appreciate it. As always, thanks to the SO community for all your help.
tl;dr - UICollectionView with custom layout (subclass of UICollectionViewLayout) randomly loses all data in the view. Goes completely blank. .reloadData()
is called on viewDidAppear
and does not fix. Stumped as to the cause.
Section 0, Item 0 is one type of cell and is kept in the top left at all times. As you scroll, the cell is moved with you and kept in the top left corner.
Section 0, Item 1-n are the second type of cell and are kept at the top at all times.
Section 1-n, Item 0-n are the "stations", another type of cell, and are kept along the left border at all times.
All other cells are the "shows" and the fourth type of cell and are mapped to a specific x,y coordinate based on time and station.
This collectionView is very performant and has allowed us to give a great user experience for the most part. However, when completely loaded up with 10 days worth of data. It uses a decent amount of memory. ~100-150MB depending on the lineup, number of stations, and length of shows. Shorter shows means more cells mapped out to the collection view.
When the app is running with a large chunk of the guide data, sometimes, the collectionView will hiccup and my floating headers will stop moving. Then ALL of my cells will disappear. We have affectionately started calling this the White Screen of Death as the entire collection is rendered empty. At this time, the scroll bars are still visible and you can see yourself scrolling around, but no cells are loaded.
We've created the problem a few times with a device connected to Xcode's debugger and have some data. The data for populating all the cells is still available from debugger. cellForItemAtIndexPath
can be called from the debugger and returns a UICollectionViewCell. numberOfSections
and numberOfItemsInSections
still functions and shows correct numbers when printing from debugger. cellForItemAtIndexPath
function is never called after collection goes empty. Breakpoint in this method will never be hit. Layout is still accessible and variables can be accessed with no problems.
Now for the things that seem abnormal, but we don't know what to do with.
collectionView.contentSize
and collectionView.collectionViewLayout.contentSize
are different. On a non-broke device, they are the same when looking through the debugger.
collectionView.visibleCells
returns an empty array. This would technically be correct as our collection is in white screen of death mode... but we have sections and items in the sections, so not sure about that.
The problem is intermittent, but we can reproduce usually a few times an hour if we are trying hard enough. We think it's memory related, because it never happens on the simulator. Only on physical devices.
Since this is an unreleased app for a client, I've blacked out their navigational controls.
Has anyone seen a similar problem? Does anyone have any ideas for what to try next?
Last note, THANK YOU FOR READING THIS FAR! :)
A layout object that organizes items into a grid with optional header and footer views for each section.
A layout object that manages the layout-related attributes for a given item in a collection view.
Overview. A compositional layout is a type of collection view layout. It's designed to be composable, flexible, and fast, letting you build any kind of visual arrangement for your content by combining — or compositing — each smaller component into a full layout.
Add a CollectionView by pressing command shift L to open the storyboard widget window. Drag the collectionView onto the main view controller. Add constraints to the UICollectionView widget to ensure that the widget fills the screen on all devices. The foundation is now set up in the storyboard.
Seeing as someone just upvoted this question, I thought I would come back and post the resolution we landed on.
Our collectionView was calling into a singleton that was managing a large amount of data for the application. The singleton was being populated in the background as the users scrolled to keep data in the view. These background updates to the singleton were all being managed via background threads running in low priority. We built the threads to update the data stores first, then update the accessor methods to know about the new data. This was our attempt at thread safety, as the accessors wouldn't know there was any other data than what already existed until after the data was done being modified. However, this did not work. More details below.
After testing extensively with multiple devices, we realized our problem showed up in a very high volume on certain days, we took this data and started analyzing the collectionView cells for the given dates, we came to find out that the days when the problem showed up regularly, there were cells that were being generated that had very large sizes, for example, a single cell in a row might span the width of 4-5 iPhones. After some searching, we found this was a common problem people had and most just downsized their cells. Our cells however are linked to times and lengths.
More research was put forth and we eventually took a swing at removing all threading. This meant that when our user hit the end of the collection, we would push a modal view that showed an activity indicator and blocked user input while the singleton was updated. This IMMEDIATELY resolved our problem.
The notes from our research:
Hope this helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With