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

Proper way to invalidate collection view layout upon size change

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

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

我也尝试过子类化 UICollectionViewFlowLayout 并将 shouldInvalidateLayoutForBoundsChange 重写为 return 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 中再次使布局无效,这会使用正确的集合视图边界,从而解决以模态呈现的视图控制器旋转的问题。但这应该不是必需的,也许还有其他情况下它的大小不合适。

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