-->

Resizing UICollectionViewCell After Data Is Loaded

2019-03-26 21:42发布

问题:

This question has been asked a few times but none of the answers are detailed enough for me to understand why/how things work. For reference the other SO questions are:

How to update size of cells in UICollectionView after cell data is set?

Resize UICollectionView cells after their data has been set

Where to determine the height of a dynamically sized UICollectionViewCell?

I'm using MVC but to keep things simple lets say that I have a ViewController that in ViewWillAppear calls a web service to load some data. When the data has been loaded it calls

[self.collectionView reloadData]

The self.collectionView contains 1 UICollectionViewCell (let's call it DetailsCollectionViewCell).

When self.collectionView is being created it first calls sizeForItemAtIndexPath and then cellForItemAtIndexPath. This causes a problem for me because it's only during cellForItemAtIndexPath that I set the result of the web service to DetailsCollectionViewCell via:

    cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"detailsCell" forIndexPath:indexPath];
    ((DetailsCollectionViewCell*)cell).details = result;

DetailsCollectionViewCell has a setter for the property details that does some work that I need to happen first to know what the correct cell size should be.

Based on the linked questions above it seems like the only way to fire sizeForItemAtIndexPath after cellForItemAtIndexPath is to call

[self.collectionView.collectionViewLayout invalidateLayout];

But this where the other questions don't work for me because although it calls sizeForItemAtIndexPath and allows me to grab enough information from DetailsCollectionViewCell to set the correct height it doesn't update the UI until after the user scrolls the UICollectionView and my guess is that it has something to do with this line from the documentation

The actual layout update occurs during the next view layout update cycle.

However, i'm stumped on how to get around this. It almost feels like I need to create a static method on DetailsCollectionViewCell that I can pass the web service result to during the first sizeForItemAtIndexPath pass and then just cache that result. But i'm hoping there is a simple solution to having the UI automatically update instead.

Thanks,

p.s. - First SO question so hope i followed all the rules correctly.

回答1:

Actually, from what I found, calling to invalidateLayout will cause calling sizeForItemAtIndexPath for all cells when dequeuing next cell (this is for iOS < 8.0, since 8.0 it will recalculate layout in next view layout update).

So the solution i came up with, is subclassing UICollectionView, and overriding layoutSubviews with something like this:

- (void)layoutSubviews
{
    if ( self.shouldInvalidateCollectionViewLayout ) {
        [self.collectionViewLayout invalidateLayout];
        self.shouldInvalidateCollectionViewLayout = NO;
    } else {
        [super layoutSubviews];        
    }
}

and then calling setNeedsLayout in cellForItemAtIndexPath and setting shouldInvalidateCollectionViewLayout to YES. This worked for me in iOS >= 7.0. I also implemented estimated items size this way. Thx.



回答2:

Here my case and solution.

My collectionView is in a scrollView and I want my collectionView and her cells to resize as I'm scrolling my scrollView.

So in my UIScrollView delegate method : scrollViewDidScroll :

[super scrollViewDidScroll:scrollView];

if(scrollView.contentOffset.y>0){

    CGRect lc_frame = picturesCollectionView.frame;
    lc_frame.origin.y=scrollView.contentOffset.y/2;
    picturesCollectionView.frame = lc_frame;
}
else{

    CGRect lc_frame = picturesCollectionView.frame;
    lc_frame.origin.y=scrollView.contentOffset.y;
    lc_frame.size.height=(3*(contentScrollView.frame.size.width/4))-scrollView.contentOffset.y;
    picturesCollectionView.frame = lc_frame;

    picturesCollectionViewFlowLayout.itemSize = CGSizeMake(picturesCollectionView.frame.size.width, picturesCollectionView.frame.size.height);
    [picturesCollectionViewFlowLayout invalidateLayout];
}

I had to re set the collectionViewFlowLayout cell size then invalidate his layout. Hope it helps !