negative or zero sizes are not supported in the flow layout

Zhang picture Zhang · Oct 8, 2014 · Viewed 17.3k times · Source

I got a rather complicated collectionView cell and I've noticed that if I scroll really fast on my collectionView it crashes the app.

One of the error I got is this:

negative or zero sizes are not supported in the flow layout

I noticed if I just return a float value e.g. 500, in my UICollectionView sizeForItemAtIndexPath: method, it doesn't crash.

My collection view has dynamic cell heights.

I'm parsing HTML Attributed string in my sizeForItemAtIndexPath: method using this library:

https://github.com/mmislam101/HTMLAttributedString

Anyone know what causes the above error message to occur in particular?

Update

The other error I also see in my Bugsense report related to this crash is:

-[__NSArrayM objectAtIndex:]: index 2 beyond bounds [0 .. 1]

This happens when I'm scrolling too fast = /

Update 2

Bugsense stacktrace shows the crash order method calls is:

1) collectionView:layout:sizeForItemAtIndexPath:

2) calculateFeedCellHeightForIndexPath: (this is one of my own method, not Apple's)

3) dynamicHeightForHTMLAttributedString:UsingWidth:AndFont: (this is one of my own method, not Apple's)

4) HTMLAttributedString attributedStringWithHtml:andBodyFont: line 35

5) HTMLAttributedString attributedString line 79

6) scrollViewDidScroll: line 174

7) setFeedFooterHeight:animated: line 124

My setFeedFooterHeight:animated: method is:

-(void)setFeedFooterHeight:(CGFloat)newHeight animated:(BOOL)animated
{
    footerHeightConstraint.constant = newHeight;

    if(animated)
    {
        [UIView animateWithDuration:0.35 animations:^{
            [self layoutIfNeeded];  // <------ crash on this line it seems
        }];
    }
    else
    {
        [self.feedFooterView layoutIfNeeded];
    }
}

However, when I run the app straight from Xcode rather than Testflight as above, Xcode stops at step 5 above, which yields this piece of code:

- (NSAttributedString *)attributedString
{
    __block NSString *css       = @"<style>";

    [_cssAttributes enumerateObjectsUsingBlock:^(NSString *cssAttribute, NSUInteger idx, BOOL *stop) {
        css = [css stringByAppendingString:cssAttribute];
    }];

    css                         = [css stringByAppendingString:@"</style>"];
    NSString *htmlBody          = [_html stringByAppendingString:css];

    NSStringEncoding encoding   = NSUnicodeStringEncoding;
    NSData *data                = [htmlBody dataUsingEncoding:encoding];

    NSDictionary *options       = @{NSDocumentTypeDocumentAttribute         : NSHTMLTextDocumentType,
                                    NSCharacterEncodingDocumentAttribute    : @(encoding)};

    // app crashes here on this next line.
    NSAttributedString *body    = [[NSAttributedString alloc] initWithData:data
                                                                options:options
                                                     documentAttributes:nil
                                                                  error:nil];

    return body;
}

I read on other threads something about wrapping uicollectionview reload until uicollection.isTracking becomes false?

I tried that but didn't seem to help.

Update 3

OK, I accidentally stumble upon the cause of that error.

It's related to the [collectionView.collectionFlowLayout invalidateLayout]; call.

I added a 1.0 second delay and the problem appears to be gone.

Answer

Philip McDermott picture Philip McDermott · Oct 10, 2014

I think this is an iOS 8 bug related to UICollectionView's delegate/datasource methods, and / or NSAttributedString.

I can get this same crash in one project by creating an NSAttributedString from HTML in sizeForItemAtIndexPath: If I create an attributed string first in numberOfRows: this stops the crash: so likely issue is something in UIKit not being initialised properly, or a buffer is being shared between the HTML parser and one of the UICollectionView sizing methods.

Note: on iOS 7.1 I get a different exception: "UICollectionView recieved layout attributes for a cell with an index path that does not exist:"


Try this experiment: call something like:

NSData *data = [@"<a href=\"http://www.google.com\">link</a>" dataUsingEncoding:NSUnicodeStringEncoding];
NSAttributedString *s = [[NSAttributedString alloc] initWithData:data options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType}  documentAttributes:nil error:nil];

in, say:

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section

This seems to force something (a parser perhaps?) to be initialised, such that when I then use similar code in:

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath

it doesn't crash. Crazy town.

I'm going to download 8.1 beta 2 and test there, and will report back.