更改尺寸后使集合视图布局无效的正确方法 [英] Proper way to invalidate collection view layout upon size change

查看:81
本文介绍了更改尺寸后使集合视图布局无效的正确方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在实现一个集合视图,其项目根据集合视图的范围来确定大小.因此,当大小发生变化时,例如由于旋转设备,我需要使布局无效,以便调整单元格的大小以考虑新的集合视图范围.我已经通过viewWillTransitionToSize API做到了这一点.

I'm implementing a collection view whose items are sized based on the bounds of the collection view. Therefore when the size changes, due to rotating the device for example, I need to invalidate the layout so that the cells are resized to consider the new collection view bounds. I have done this via the viewWillTransitionToSize API.

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

This works well until the user presents a modal view controller over the view controller that contains the collection view, rotates the device, then dismisses it. When that occurs the item size hasn't updated to the appropriate size. viewWillTransitionToSize is called and the layout is invalidated as expected, but the collection view's bounds is still the old value. For example when rotating from portrait to landscape the collection view bounds value still has its height greater than the width. I'm not sure why that's the case, but I'm wondering if this is the best way to invalidate upon size change? Is there a better way to do it?

我还尝试了对UICollectionViewFlowLayout进行子类化,并覆盖了shouldInvalidateLayoutForBoundsChange以返回YES,但是由于某些原因,即使没有模式显示也无法旋转.它也没有使用正确的集合视图范围.

I have also tried subclassing UICollectionViewFlowLayout and overriding shouldInvalidateLayoutForBoundsChange to return YES, but for some reason this doesn't work even rotating without a modal presentation. It doesn't use the proper collection view bounds either.

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

我也尝试在完成块中使它无效,但是它仍然没有使用正确的集合视图范围.

I've also tried invaliding it in the completion block but it still doesn't use the proper collection view bounds.

如果我再次在viewWillAppear中使布局无效,则会使用正确的集合视图范围,从而解决了使用模态呈现的视图控制器旋转时出现的问题.但这不是必须的,也许在其他情况下,此文件的大小也不正确.

If I invalidate the layout again in viewWillAppear, this does use the proper collection view bounds which resolves the issue with rotating with the modally presented view controller. But this shouldn't be necessary, perhaps there are other situations where this won't be sized properly.

推荐答案

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

I know what the problem is. When you call invalidateLayout inside animateAlongsideTransition (either in the animation block or the completion block), it doesn't actually recalculate the layout if there is a modal view controller presented over full screen (but it will if it's over current context). But it will invalidate it if you invalidate it outside of the animation block like I was doing. At that time however the collection view hasn't laid out for the new size, which explains why I was seeing the old value for its bounds. The reason for this behavior is invalidateLayout does not immediately cause the layout to be recalculated - it is scheduled to occur during the next layout pass. This layout pass does not occur when there's a modally presented view controller over full screen. To force a layout pass, simply call [self.collectionView layoutIfNeeded]; immediately after [self.collectionView.collectionViewLayout invalidateLayout];, still within the animateAlongsideTransition block. This will cause the layout to be recalculated as expected.

这篇关于更改尺寸后使集合视图布局无效的正确方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆