通过切换布局触发动画UICollectionView单元格调整大小

时间:2013-11-20 04:12:47

标签: objective-c animation uicollectionview

我正在尝试在各种UICollectionViewFlowLayout子类之间进行动画处理。每个流布局指定不同的项目大小。一个显示一个包含3个项目的网格,另一个显示包含2个项目的网格,第三个显示只有一个项目(看起来像UITableView)。

我正在调用setCollectionViewLayout:animated:来做这件事。单元格大小的动画效果很好。然而,子视图有点混乱。每个单元格都包含一个图像和一个标签(暂时删除了标签)。我希望图像在大小变化时为其大小的变化设置动画,同样也是标签。

如果我使用自动布局(目前还不是很好)来布局我的单元格的子视图,那么在动画开始时,图像会调整为目标单元格框架的大小。我理解UIView动画是如何工作的,因为他们会立即更改动画属性,动画在表示层完成,但我希望自动布局能以某种方式有所不同。

如果我删除自动布局并在initWithFrame中执行以下操作:

[[self imageView] setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleRightMargin];

然后图像子视图的大小动画很好,但是在循环几个动画后,图像子视图的大小和位置在某些情况下是不正确的。

我已尝试在layoutSubviews中布置子视图,但这会产生与AutoLayout相同的结果。

我在github上看到的所有在UICollectionViewLayouts之间制作动画的演示只有彩色背景或填充整个单元格的子视图。有没有人设法使这个工作?

下面是正在发生的事情的图像(红线是UIImageView的边框):

First Layout

Second Layout

Third Layout

Back to first layout, but subview frames are wrong

3 个答案:

答案 0 :(得分:39)

要使用AutoLayout,您需要将以下内容放在UICollectionViewCell子类中:

- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes {
    [UIView animateWithDuration:animationDuration animations:^{
        [self layoutIfNeeded];
    }];
}

默认动画持续时间约为0.33秒(由实验确定)。

答案 1 :(得分:4)

所以我采用的解决方案是关闭自动布局(正如Marty所指出的那样),但我也删除了所有自动调整大小的蒙版。我在UICollectionViewCell子类中为它们将要显示的每个UICollectionViewFlowLayout创建了三个不同的硬编码布局。

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;
    }
}

然后当用户选择循环布局按钮时,我前进到下一个布局并调用我的ViewController中的以下内容(具有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];

            }];
        }];

    }
}

这使我可以完全控制Cell中每种类型的FlowLayout子类的每个布局。动画很流畅,我也可以在特定的布局中淡出任何我不想要的视图。

这样做的结果是UICollectionView,其行为与iPhone Ebay Apps网格视图行为非常相似,但在我看来更好。

很抱歉,我无法粘贴任何代码或将其放在GitHub上,因为它来自客户端代码。 我很乐意回答任何问题。

干杯, 布雷特

答案 2 :(得分:1)

你好Brett在找到答案之后我发现这对我有帮助:

UICollectionView cell subviews do not resize

我最终关闭了自动布局,只使用了IB的传统自动调整大小功能。

努力享受!希望它能帮到你!

编辑:抱歉,我刚刚意识到你是以编程方式完成此操作,是否可以在完成块上设置动画集布局中重新加载数据来重绘单元格?按照这个:

UICollectionView cells, auto layout and collectionview layout changes