Animating UICollectionView cell resize triggered by switching layouts
animation, objective-c, uicollectionview
Solution
So the solution I went with was to turn off Auto Layout (as pointed out by Marty), but I also removed all Auto resizing masks too. I created three different hardcoded layouts in the UICollectionViewCell subclass for each UICollectionViewFlowLayout they were going to be displayed in.
GSProductCollectionViewCell.m
-(void)setLayoutForWideLayoutWithDestinationSize:(CGSize)size
{
[[self imageView] setFrame:CGRectMake( 5.0f, 5.0f, (int)(size.width * 0.3f), size.height - 10.0f)];
[[self titleLabel] setFrame:CGRectMake( self.imageView.right + 5.0f, 5.0f, size.width - self.imageView.right - 15.0f, 25.0f)];
[[self titleLabel] setFont:[UIFont fontWithName:self.titleLabel.font.fontName size:12.0f]];
[[self titleBackgroundView] setFrame:self.titleLabel.frame];
[[self titleBackgroundView] setAlpha:1.0f];
[[self descriptionLabel] setAlpha:1.0f];
[[self descriptionLabel] setFrame:CGRectMake( self.titleLabel.left, self.titleLabel.bottom + 5.0f, self.titleLabel.width, 25.0f)];
[...]
}
-(void)setLayoutForSqaureLayoutWithDestinationSize:(CGSize)size
{
[[self imageView] setFrame:CGRectMake( 5.0f, 5.0f, size.width - 10.0f, size.height - 10.0f - 30.0f)];
[[self titleLabel] setFrame:CGRectMake( 5.0f, size.height - 30.0f, size.width - 10.0f, 25.0f)];
[[self titleLabel] setFont:[UIFont fontWithName:self.titleLabel.font.fontName size:10.0f]];
[[self titleBackgroundView] setFrame:self.titleLabel.frame];
[[self titleBackgroundView] setAlpha:0.0f];
[[self descriptionLabel] setAlpha:0.0f];
[[self descriptionLabel] centerView];
[...]
}
-(void)setLayoutWithFrameSize:(CGSize)size forStyle:(GSProductLayoutStyle)layoutStyle
{
switch (layoutStyle)
{
case kGSProductLayoutStyleGrid3:
case kGSProductLayoutStyleGrid2:
[self setLayoutForSqaureLayoutWithDestinationSize:size];
break;
case kGSProductLayoutStyleList:
[self setLayoutForWideLayoutWithDestinationSize:size];
break;
default:
break;
}
}
Then when the user selects the cycle layout button I advance to the next layout and call do the following in my ViewController (Has a UICollectionView):
-(void)setLayoutStyle:(GSProductLayoutStyle)layoutStyle
{
if (_layoutStyle != layoutStyle)
{
_layoutStyle = layoutStyle;
UICollectionViewFlowLayout *layout;
switch (_layoutStyle)
{
case kGSProductLayoutStyleGrid3:
layout = [GSProductCollectionViewGridLayout new];
break;
case kGSProductLayoutStyleGrid2:
layout = [GSProductCollectionViewGridTwoLayout new];
break;
case kGSProductLayoutStyleList:
layout = [GSProductCollectionViewListLayout new];
break;
default:
layout = [GSProductCollectionViewGridLayout new];
break;
}
CGSize itemSize = layout.itemSize;
[self setCollectionViewLayout:layout animated:YES];
[[self visibleCells] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[(GSProductCollectionViewCell*)obj setLayoutStyle:_layoutStyle];
[UIView animateWithDuration:0.3f animations:^{
[(GSProductCollectionViewCell*)obj setLayoutWithFrameSize:itemSize forStyle:self.layoutStyle];
}];
}];
}
}
This gives me complete control over each layout in the Cell for each type of FlowLayout subclass. The animations are smooth and I can also fade out any views which I don't want in a particular layout.
The result of this is a UICollectionView which behaves a lot like the iPhone Ebay Apps grid view behaviour, but better in my opinion.
Sorry I can't paste any more code or put it on GitHub as it is from Client Code. I am happy to answer any questions though.
Cheers, Brett
Problem
I am trying to animate between various UICollectionViewFlowLayout subclasses. Each flow layout specifies a different item size. One displays a grid with 3 items across, another a grid with 2 items across, and the third with only 1 item across (Looks like a UITableView). I am calling setCollectionViewLayout:animated: to do this. The animation of the cell sizes works perfectly. However the subviews are a bit of a mess. Each cell contains an image and a label (Have removed the label for now). I want the image to animate its change in size as the cell changes in size, likewise the label. If I use auto layout (not very good at this yet) to layout the subviews of my cell then at the start of the animation the image is resized to the destination cell frame. I understand how UIView animations work in that they change the animated property straight away and the animation is done in the presentation layer, but I expected auto layout to work differently somehow. If I remove auto layout and do the following in initWithFrame: ``` [[self imageView] setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleRightMargin]; ``` then the size of the image subview animates quite nicely, however after cycling through a few animations the image subview size and location is incorrect in some cases. I've tried laying out the subviews in layoutSubviews, but this gives the same result as AutoLayout. All of the demos I've seen on github for animating between UICollectionViewLayouts have just a coloured background or a subview which fills the entire cell. Has anyone managed to get this working? Below are images of what is occurring (red line is border of UIImageView): First Layout Second Layout Third Layout Back to first layout, but subview frames are wrong