UICollectionView assertion error on stale data - uicollectionview

In the course of trying to unload one batch of images from my collection view and then replace them with another batch, I run into an error where, depending on whether the original or subsequent group of images was more or less than the intended replacement, an assertion error occurs which says:
*** Assertion failure in -[UICollectionViewData validateLayoutInRect:],
/SourceCache/UIKit_Sim/UIKit-2891.1/UICollectionViewData.m:341
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException',
reason: 'UICollectionView recieved layout attributes for a cell with an
index path that does not exist: <NSIndexPath: 0xb141c60> {length = 2, path = 0 - 2}
In this case the existing list of images count was 5 and the new list of images count was 2. So, when it got to the third image - the exception occurred - indicating that the UI CollectionViewDataDelegate did not know of the change in the data stream.
Any suggestions about how to make sure the new images will be referenced by the UICollectionView? Of course I have called 'reloadData'…
Thank you

I run in the same problem. Code runs under 6.1 and crashes under 7.0
I solved the issue the following way:
In the function
-(NSInteger) numberOfSectionsInCollectionView:(UICollectionView *)collectionView
I call
[myCollectionView.collectionViewLayout invalidateLayout];
That´s all.

With iOS 10 and 11 this helps:
collectionView.reloadData()
collectionView.collectionViewLayout.invalidateLayout()
Invalidate layout should be AFTER reload data.

Both Dominic Sander and user1544494 are right and their solutions are good.
Unfortunately, I've noticed that if you set minimumLineSpacingForSectionAtIndex or minimumInteritemSpacingForSectionAtIndex, the look of your collectionView is going to break (sooner or later).
Putting invalidateLayout in viewWillLayoutSubviews answers this question and helps preserving the look of viewCollection.
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
[viewCollection.collectionViewLayout invalidateLayout];
}

It's simple.
Just like that below sentence.
'UICollectionView recieved layout attributes for a cell with an
index path that does not exist: <NSIndexPath: 0xb141c60> {length = 2, path = 0 - 2}
It's mean that there is no indexPath(0,2) on dataSouce.
But, your UICollectionViewLayout return a UICollectionViewLayoutAttributes for indexPath(0,2).
You should return UICollectionViewLayoutAttributes just only exists on dataSouce.
I think that It was changed from iOS7.

My problem was that I had two UICollectionViews inside one UIViewController. And I had both UICollectionViews connected to the same UICollectionViewLayout subclass. I fixed this problem by changing each UICollectionView to have their own UICollectionViewLayout subclass.
Source: This Question

I fixed this crash by updating my collection view data source :
- (NSInteger)collectionView:(UICollectionView *)collectionView
numberOfItemsInSection:(NSInteger)section
{
[collectionView.collectionViewLayout invalidateLayout];
return collectionArray.count;
}

I had the same crash.
In my app, the problem was that I didn't empty the array with UICollectionViewLayoutAttributes. I'm using it in the prepareLayout() method to store the layout attribute for each cell.
var itemAttributes: Array<UICollectionViewLayoutAttributes> = Array<UICollectionViewLayoutAttributes>()
With just self.itemAttributes.removeAll() in the first line of prepareLayout, it works.

I've experienced this problem after modifying the contents of the Collection View. The solution that worked in my case was to invalidate the layout after reload. Doing it before the reload will not work.
[collectionView reloadData];
//forces the layout attributes to be recalculated for new data
[collectionView.collectionViewLayout invalidateLayout];

The solution I found was to ensure the indexPath I was creating in the layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? method is valid for the row. Previously, I was using (where i is my loop counter):
var indexPath = NSIndexPath(index: i)
var attributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)
But updating it to use the following solved it:
var indexPath = NSIndexPath(forRow: i, inSection: 0)!
var attributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)

I was able to solve this issue by creating a subclass of UICollectionViewFlowLayout and overriding this method to return YES:
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return YES;
}

I have encountered with this issue and it was pretty annoying. My fix is to forget about UICollectionViewController and use instead regular UIViewController with UICollectionView inside.

Make sure you update the contentSize of your collectionViewLayout. So after getting new images (2, instead of 5), recalculate the contentSize and set it.

I've had this bug too and found a workaround. For me, the UICollectionView was launching this under iOS 7, working perfectly on iOS 8.
Check this article: What's causing this iOS crash? UICollectionView received layout attributes for a cell with an index path that does not exist
In 2 words: Auto-Layout. Disable it on the view containing the UICollectionView and for me, it worked.

The collectionViewLayout caches the layout attributes.
In viewwillappear . Create a new instance of collectionViewLayout and assign it to collectionview.collectionViewLayout
In this way all the cached attributes will purge before the reload
Your problem might be resolved. Worked for me , especially when you are using other collectionViewLayout libraries.

I had similar problem (using Swift2.0, XCode 7).
The app crashed with UICollectionView received layout attributes for a cell with an index path that does not exist...
In my case, since I used the storyboard, it just turned out that I forgot to connect the IBOutlet that was defined in my viewController with the actual collectionView defined in the storyboard. Connecting the two fixed the problem.

I have figured it out.
If you are using nib/xib to organize your UITableViewCell and nested UICollectionView,you can avoid this error by overriding this method.
- (void)prepareForReuse {
[super prepareForReuse];
[self.collectionView.collectionViewLayout invalidateLayout];
}
Hope it helps.

It's mean that there is no indexPath(0,2) on dataSouce. But, your UICollectionViewLayout return a UICollectionViewLayoutAttributes for indexPath(0,2). By TopChul
That's Right! For Me, the issue happened because I use same collection layout(instance) for two collectionView! So that layout confused between two collection View.
It work fine after I use different layout between different collection view.

I ran into something similar when trying to duplicate a collection view into another storyboard.
'UICollectionView received layout attributes for a cell with an index
path that does not exist: {length = 2, path
= 1 - 0}'
At first, I am looking for a quick fix. Tried copy paste various StackOverflow answers.
But I wrote my own layout class. So I try to debug discreetly, it could be my implementation to blame, right? Found that numberOfSections method was never called. The collection view assumed that it had only one section.
Then I found view controller class forgot to conform to UICollectionViewDataSource. Though the dataSource was hooked up in the storyboard but probably the view controller class will be downcast, like if let ds = dataSource as? UICollectionViewDataSource {ds.numberOfSections...}, which would fail silently.
So I added the conformance to UICollectionViewDataSource and everything works fine. My guess could be inaccurate. But the lesson is whenever there's a bug you're not familiar with, settle down and understand it. UICollectionView received layout attributes for a cell with an index path that does not exist, it means exactly what it says. Not that hard right? Don't try to find a silver bullet, like many of the answers here. They are all great but your code is the real battle place.

I run into the same problem when I use UICollectionViewFlowLayout as CollectionView's collectionViewLayout.
Declare the parent viewController implemented UICollectionViewDelegateFlowLayout and assign it as collectionView's delegate can solve this problem.

In my case i had a custom UICollectionViewFlowLayout. After removing cells from the collectionView the app crashed. The fix was to removeAll() previously calculated attributes. So, first line after override func prepare() is arrayHoldingYourAttributes.removeAll().

Hollo, I have the same issue while insert a collectionView into another collectionView and reloadData in main queue. Finally I reloadData before a new data convert to collectionView.
dispatch_async(dispatch_get_main_queue(), ^{
[_collectionView reloadData];
});
_notes = notes;
[_collectionView reloadData];

Related

iOS7 Keyboard Behavior with SearchBar/UITableView: Offset on secondary view appearances

Problem
I am having a rather big issue with the iOS7 keyboard appearance. I have a Searchbar on a UIViewController with TableView Delegation/Data Source setup (I am using the self.searchDisplayController delegates as well). I segue from this scene to a prototype tableview to show the results.
Here is the issue:
On first load I can see the keyboard being displayed when I tap into the text field of the UISearchBar. I can type and perform a search with the results being shown in the next scene.
I've added NSNotifications to view the keyboard properties in local methods keyboardWillShow and keyboardWasShown. I can see on the first scene appearance (after the view is completely loaded):
I segue to the result tableview at this point and when I navigate back and touch the text field, my keyboard shows up either fully or partially off-screen:
When I look at the keyboardWillShow notification at this point I can see that my keyboard values are incorrect:
I've researched and tried many possibilities including:
Added the following to my main view controller:
-(BOOL)canResignFirstResponder
{
return YES;
}
-(BOOL)canBecomeFirstResponder
{
return YES;
}
Configured the following in my view did load
self.searchDisplayController.searchBar.spellCheckingType = UITextSpellCheckingTypeNo;
self.searchDisplayController.searchBar.autocapitalizationType= UITextAutocapitalizationTypeNone;
self.searchDisplayController.searchBar.autocorrectionType = UITextAutocorrectionTypeNo;
self.searchDisplayController.searchBar.keyboardType = UIKeyboardTypeDefault;
Put in standard stubs for:
-(void)searchDisplayController:(UISearchDisplayController *)controller didShowSearchResultsTableView:(UITableView *)tableView
-(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
I've noticed that if I choose a Partial Curl as my segue mode, the keyboard remains accessible when I roll back to the main view controller (but then it was never fully off screen in that case). However if I move from the results tableview to a detail scene and then navigate back to the main view controller, the keyboard appears off-screen again.
Question
Is there a method I can use to intercept the misplaced keyboard so that it displays in the default location?
NB: Along these lines, I have created a NSDictionary property to hold the initial userInfo values with the correct keyboard placement. I am not sure how to reassign these values to get the keyboard to return to it's original placement.
BTW - This seems a bit of a hack to get the keyboard fixed due to a bug in IB, is there some other way that I can try to remedy the situation?
Thanks in advance for any feedback!
Solution
This was such an obscure issue that I'm sharing the solution to save the next person some effort. Like most programming issues, it turns out this one was self-inflicted. In my original iteration of this project I had turned off rotational support as I am learning auto-layout and I wanted to ease into the transition from Springs and Struts. Somehow between the start of the project and the code release I ended up with this bit of code in the Main Scenes' View Controller.
//BAD
- (NSUInteger) supportedInterfaceOrientations
{
return !UIInterfaceOrientationPortraitUpsideDown;
}
instead of returning a valid enumeration like...
//OK
- (NSUInteger) supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAll;
}

iOS: More tab crashes on my subclassed UITabBarController on iOS 7.1

I simply updated to iOS 7.1 and I get an unrecognized selection error for a function called "_layoutCells".
I have a simple subclass of UITabBarController.
Note that this is a hack to avoid a bad crash until a better solution or explanation is found. I though I should share it.
Simply add the following method to your UITabBarController subclass implementation:
- (void) _layoutCells
{
// HACK ALERT: on iOS 7.1, this method will be called from deep within the bowels of iOS. The problem is that
// the method is not implemented and it results in an unrecognized selected crash. So we implement it...
//
// What could go wrong?
}
Thanks to GenesisST for giving his answer, but I know methods are called for a reason. And usually layoutCells will call layout for all subviews. While I rather wait for an answer from Apple, I like other people need to submit my app within a given timeline.
In my case, I was getting this error due to some hacks. I had replaced the UIMoreNavigationController delegate, which is an undocumented class by Apple, so I could expect errors. I am doing this hack to extend the More tab's functionality, and this error only occurs when I change the moreNavigationController tableView's delegate.
So I store their delegate, change it, then call _layoutCells to their delegate when iOS calls it on my class.
- (void)_layoutCells
{
if([self.moreTableViewDelegate respondsToSelector:#selector(_layoutCells)]){
[self.moreTableViewDelegate performSelector:#selector(_layoutCells)];
}
}
I don't know if this apply's to anyone here, but just in case someone else comes to SO with my edge case.
I've had the same issue in my app where I have provided custom delegate/datasource to the more tableview controller. I haven't figured out why, but it seems that _layoutCells method is invoked on the more tableview controller.
I fixed it, adding this method:
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
// self.viewController is my tabBarController
UINavigationController* moreNavigationController = self.viewController.moreNavigationController;
// Retrieve the more list controller (it is the first in the hierarchy)
id moreListController = moreNavigationController.viewControllers.firstObject;
Class moreTableViewClass = [moreListController class];
if (moreTableViewClass) {
return [moreTableViewClass instanceMethodSignatureForSelector:aSelector];
}
return nil;
}
I've done various test and it seems a reliable workaround. But if you'll find better solution... share it!

Reloading UICollectionView causing flickering

I have subclassed a CollectionView and using it using to display a collection of images in different categories. For example: Most Popular, Most Viewed. I access each category/page through a TabbarController.
The classes I have are:
Main class - MediaList - Base class for fetching and displaying media in a collection view depending on what type of media is requested.
SubClasses - MostPopular and MostViewed are both very light classes that inherit from the main class.
The problem is that when I move from MostPopular to MostViewed, the collection view initially displays the last category of pictures and then refreshes to the new...As a result you see a flicking effect.
Sample code from MediaList
- (void)viewDidLoad
{
[super viewDidLoad];
self.mediaCollection.dataSource = self;
self.mediaCollection.delegate = self;
[self getMedia];
}
(void) getMedia
{
NSLog(#"///////////////////////// GETTING MEDIA /////////////////////////");
XMLGenerator * sharedXmlGen = [XMLGenerator sharedXmlGenerator];
GlobalVars * sharedGlobalVars = [GlobalVars sharedGlobalVars];
sharedXmlGen.assetFlag = _contentFilter;
NSLog(#"The filter is: %#", _contentFilter);
[sharedXmlGen getPhotoVideoWithCallBackFunction:^{
[_mediaCollection performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:YES];
}];
}
Sample code from SubClass (MostPopular):
- (void)viewDidLoad
{
[super setContentFilter:FILTER_POPULAR]; //Set the filter for the type of content
[super viewDidLoad];
// Do any additional setup after loading the view.
}
Two clarifications:
I should add that I am trying to re-use as much code as possible without having to create new views on the storyboard or controllers.
Because the media returned is very dynamic and can be updated regularly, I have had to reload my data (using getMedia) on viewDidAppear. This now means each selection of a particular page will result in the flickering when the new view is loaded...
Updates
- After further investigation it looks like this has nothing to do with subclassing the uicollectionview. The issue instead is just reloading data into the uicollectionview. To be sure, I just clicked on one of the two subclassed views. I also added a reload method to the viewdidAppear method just to see the issue get even worse. It's also much easier to see that the issue is that last set of images in the collection view initially displays the last category of pictures before the new one refreshes. Then once the collection is refreshed I get the flicker.
*Updates Oct 4 *
Starting to think my best solution is to display a waiting indicator and block the page while loading. I did try setting the uicollectionview to nil, and believe that will solve the problem as well, but not sure it will be straightforward to reset.
Although it may not be immediately apparent in the code above the root cause was actually the getMedia call. All of the subclassing etc. works perfectly fine.
Specifically having the reload method in the getMedia call and was creating the issue.
[_mediaCollection performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:YES];

Stop UIPopover from dismissing automatically part II

Sorry I previously posted a question here, but The answers I got didn't work (probably my fault).
Basically, I want to stop a UIPopover from dismissing automatically. I got to this piece of code:
- (BOOL) popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController
{
return NO;
}
which I put in, but it doesn't seem to have any effect. Is there anything else I should add?
am I putting it in the right place (I was putting it in the vc.m of the view that has the popover within it)?
Thanks Very Much,
Luke
Check to make sure the UIPopover delegate is set. You can do this in code by setting the popover instance variable:
aPopover.delegate = self;

setNeedsDisplay not working?

I have a problem redrawing a custom view in simple cocoa application. Drawing is based on one parameter that is being changed by a simple NSSlider. However, although i implement -setParameter: and -parameter methods and bind slider's value to that parameter in interface builder i cannot seem to make a custom view to redraw itself.
The code that does redrawing is like this:
- (void)setParameter:(int)newParameter {
parameter = newParamter;
NSLog(#"Updated parameter: %d", parameter);
[self setNeedsDisplay:YES];
}
I DO get the message about setting the new parameter although the view doesn't redraw itself. Any ideas are welcome!
The usual syntax is: [self setNeedsDisplay:YES], although I would assume that that means the same thing. Are you implementing the - (void)drawRect:(NSRect)rect method, or using the drawRect: method of your superclass?
For anyone that has this problem while using an NSOpenGLView subclass, you might be forgetting to use [[self openGLContext] flushBuffer] at the end of drawRect:.
Sometimes reason can be very simple: File's owner has no connection to UIView object. i.e. it's Outlet is not setup properly.
Use IB, ctrl button and drag method :)
In the iOS 6 there isn't such function to call: setNeedsDisplay:YES. I've got the same problem, and came with this solution: https://stackoverflow.com/a/15027374/1280800 .
Hope it will help.