在大小更改时使集合视图布局无效的正确方法

Posted

技术标签:

【中文标题】在大小更改时使集合视图布局无效的正确方法【英文标题】:Proper way to invalidate collection view layout upon size change 【发布时间】:2016-05-21 20:52:19 【问题描述】:

我正在实现一个集合视图,其项目的大小基于集合视图的边界。因此,当尺寸发生变化时,例如由于旋转设备,我需要使布局无效,以便调整单元格的大小以考虑新的集合视图边界。我通过viewWillTransitionToSize API 完成了这项工作。

直到用户在包含集合视图的视图控制器上显示一个模态视图控制器,旋转设备,然后关闭它之前,这都很好。发生这种情况时,项目大小尚未更新到适当的大小。 viewWillTransitionToSize 被调用并且布局按预期无效,但集合视图的边界仍然是旧值。例如,当从纵向旋转到横向时,集合视图边界值的高度仍然大于宽度。我不确定为什么会这样,但我想知道这是否是在大小更改时失效的最佳方法?有更好的方法吗?

我也尝试子类化UICollectionViewFlowLayout 并覆盖shouldInvalidateLayoutForBoundsChange 以返回YES,但由于某种原因,即使在没有模态演示的情况下旋转也无法正常工作。它也没有使用正确的集合视图边界。

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(nonnull id<UIViewControllerTransitionCoordinator>)coordinator

    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

    [self.collectionView.collectionViewLayout invalidateLayout];

    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext>  __nonnull context) 
        [self.collectionView.collectionViewLayout invalidateLayout];
     completion:^(id<UIViewControllerTransitionCoordinatorContext>  __nonnull context) 
        //finished
    ];


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

    //collectionView.bounds.size is not always correct here after invalidating layout as explained above

我也尝试在完成块中使其无效,但它仍然没有使用正确的集合视图边界。

如果我在viewWillAppear 中再次使布局无效,这确实使用了正确的集合视图边界,从而解决了使用模态呈现的视图控制器旋转的问题。但这不应该是必要的,也许还有其他情况无法正确调整大小。

【问题讨论】:

你查看***.com/questions/29023473/…了吗? @ozgur 我已经尝试过答案是的,如上所述 您确定没有更新viewWillAppear() 中的任何内容吗?在所描述的场景中(您在模态控制器结束时更改方向)当模态视图控制器被解除时,您可能会在viewWillAppear() 中获得具有 previous 方向的框架,并在 @ 中获得正确的框架987654331@。所以通常对我有用的只是更新viewWillTransitionToSize中的布局。 @AlexStaravoitau 在viewWillAppear 中没有任何内容。 viewWillTransitionToSize 是唯一让布局无效的地方。 viewWillAppear 中再次使布局无效确实解决了模式视图控制器行为的问题。不是一个非常干净的解决方案,也许在其他情况下它也无法按预期工作? 【参考方案1】:

我知道问题出在哪里。当您在animateAlongsideTransition 内(在动画块或完成块中)调用invalidateLayout 时,如果在全屏上显示模态视图控制器,它实际上不会重新计算布局(但如果它在当前上下文中,它会重新计算)。但是,如果您像我一样在动画块之外使其无效,它将使其无效。然而,当时集合视图还没有为新尺寸布局,这解释了为什么我看到它的边界的旧值。这种行为的原因是invalidateLayout 不会立即导致重新计算布局 - 它计划在下一次布局传递期间发生。当在全屏上有模态呈现的视图控制器时,不会发生此布局传递。要强制布局通过,只需在[self.collectionView.collectionViewLayout invalidateLayout]; 之后立即调用[self.collectionView layoutIfNeeded];,仍在animateAlongsideTransition 块内。这将导致按预期重新计算布局。

【讨论】:

你的回答让我很开心..非常感谢!!

以上是关于在大小更改时使集合视图布局无效的正确方法的主要内容,如果未能解决你的问题,请参考以下文章

嵌套集合视图单元格未在方向更改时调整大小

UICollectionView 内容未正确调整大小

在集合视图的 cellForItemAtIndexPath 方法中更改框架

只想使用自动布局调整中间视图的大小

iOS中的自定大小集合视图单元格

集合视图单元格宽度不正确