在大小更改时使集合视图布局无效的正确方法
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
块内。这将导致布局按预期重新计算。
我正在实现一个集合视图,其项目的大小基于集合视图的边界。因此,当尺寸发生变化时,例如由于旋转设备,我需要使布局无效,以便调整单元格的大小以考虑新的集合视图边界。我是通过 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
块内。这将导致布局按预期重新计算。