在 UICollectionView 上动态设置布局导致莫名其妙的 contentOffset 变化 [英] Dynamically setting layout on UICollectionView causes inexplicable contentOffset change

查看:25
本文介绍了在 UICollectionView 上动态设置布局导致莫名其妙的 contentOffset 变化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据 Apple 的文档(并在 WWDC 2012 上吹捧),可以在 UICollectionView 上动态设置布局,甚至可以为更改设置动画:

According to Apple's documentation (and touted at WWDC 2012), it is possible to set the layout on UICollectionView dynamically and even animate the changes:

您通常在创建集合视图时指定一个布局对象,但您也可以动态更改集合视图的布局.布局对象存储在 collectionViewLayout 属性中.设置此属性会立即直接更新布局,而不会对更改进行动画处理.如果您想为更改设置动画,则必须调用 setCollectionViewLayout:animated: 方法.

You normally specify a layout object when creating a collection view but you can also change the layout of a collection view dynamically. The layout object is stored in the collectionViewLayout property. Setting this property directly updates the layout immediately, without animating the changes. If you want to animate the changes, you must call the setCollectionViewLayout:animated: method instead.

但是,在实践中,我发现 UICollectionViewcontentOffset 进行了莫名其妙甚至无效的更改,导致单元格移动不正确,使该功能几乎无法使用.为了说明这个问题,我将以下示例代码放在一起,这些示例代码可以附加到拖放到故事板中的默认集合视图控制器:

However, in practice, I've found that UICollectionView makes inexplicable and even invalid changes to the contentOffset, causing cells to move incorrectly, making the feature virtually unusable. To illustrate the problem, I put together the following sample code that can be attached to a default collection view controller dropped into a storyboard:

#import <UIKit/UIKit.h>

@interface MyCollectionViewController : UICollectionViewController
@end

@implementation MyCollectionViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"CELL"];
    self.collectionView.collectionViewLayout = [[UICollectionViewFlowLayout alloc] init];
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return 1;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewCell *cell = [self.collectionView dequeueReusableCellWithReuseIdentifier:@"CELL" forIndexPath:indexPath];
    cell.backgroundColor = [UIColor whiteColor];
    return cell;
}

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"contentOffset=(%f, %f)", self.collectionView.contentOffset.x, self.collectionView.contentOffset.y);
    [self.collectionView setCollectionViewLayout:[[UICollectionViewFlowLayout alloc] init] animated:YES];
    NSLog(@"contentOffset=(%f, %f)", self.collectionView.contentOffset.x, self.collectionView.contentOffset.y);
}

@end

控制器在 viewDidLoad 中设置默认的 UICollectionViewFlowLayout 并在屏幕上显示单个单元格.当单元格被选中时,控制器创建另一个默认的 UICollectionViewFlowLayout 并使用 animated:YES 标志在集合视图上设置它.预期的行为是单元格不移动.然而,实际行为是单元格滚动到屏幕外,此时甚至不可能将单元格滚动回屏幕上.

The controller sets a default UICollectionViewFlowLayout in viewDidLoad and displays a single cell on-screen. When the cells is selected, the controller creates another default UICollectionViewFlowLayout and sets it on the collection view with the animated:YES flag. The expected behavior is that the cell does not move. The actual behavior, however, is that the cell scroll off-screen, at which point it is not even possible to scroll the cell back on-screen.

查看控制台日志显示 contentOffset 发生了莫名其妙的变化(在我的项目中,从 (0, 0) 变为 (0, 205)).我发布了 非-animated case(ie animated:NO),但由于我需要动画,我很想知道是否有人对动画案例有解决方案或解决方法.

Looking at the console log reveals that the contentOffset has inexplicably changed (in my project, from (0, 0) to (0, 205)). I posted a solution for the solution for the non-animated case (i.e. animated:NO), but since I need animation, I'm very interested to know if anyone has a solution or workaround for the animated case.

附带说明一下,我已经测试了自定义布局并获得了相同的行为.

As a side-note, I've tested custom layouts and get the same behavior.

推荐答案

我已经为此烦恼了好几天,并找到了可能对我的情况有所帮助的解决方案.就我而言,我有一个折叠的照片布局,就像在 ipad 上的照片应用程序中一样.它显示相册,照片彼此叠放,当您点击相册时,它会展开照片.所以我所拥有的是两个独立的 UICollectionViewLayouts 并在它们之间切换 [self.collectionView setCollectionViewLayout:myLayout animated:YES]代码>内容偏移.我用 contentOffset 尝试了一切,但它仍然在动画期间跳跃.上面 tyler 的解决方案有效,但它仍然与动画混淆.

I have been pulling my hair out over this for days and have found a solution for my situation that may help. In my case I have a collapsing photo layout like in the photos app on the ipad. It shows albums with the photos on top of each other and when you tap an album it expands the photos. So what I have is two separate UICollectionViewLayouts and am toggling between them with [self.collectionView setCollectionViewLayout:myLayout animated:YES] I was having your exact problem with the cells jumping before animation and realized it was the contentOffset. I tried everything with the contentOffset but it still jumped during animation. tyler's solution above worked but it was still messing with the animation.

然后我注意到只有当屏幕上有几张专辑,不足以填满屏幕时才会发生这种情况.我的布局按照建议覆盖了 -(CGSize)collectionViewContentSize.当只有几个相册时,集合视图内容大小小于视图内容大小.当我在集合布局之间切换时,这会导致跳转.

Then I noticed that it happens only when there were a few albums on the screen, not enough to fill the screen. My layout overrides -(CGSize)collectionViewContentSize as recommended. When there are only a few albums the collection view content size is less than the views content size. That's causing the jump when I toggle between the collection layouts.

所以我在布局上设置了一个名为 minHeight 的属性,并将其设置为集合视图父级的高度.然后我在返回之前检查高度 -(CGSize)collectionViewContentSize 我确保高度 >= 最小高度.

So I set a property on my layouts called minHeight and set it to the collection views parent's height. Then I check the height before I return in -(CGSize)collectionViewContentSize I ensure the height is >= the minimum height.

不是一个真正的解决方案,但它现在工作正常.我会尝试将集合视图的 contentSize 设置为至少是包含视图的长度.

Not a true solution but it's working fine now. I would try setting the contentSize of your collection view to be at least the length of it's containing view.

如果您从 UICollectionViewFlowLayout 继承,Manicaesar 添加了一个简单的解决方法:

edit: Manicaesar added an easy workaround if you inherit from UICollectionViewFlowLayout:

-(CGSize)collectionViewContentSize { //Workaround
    CGSize superSize = [super collectionViewContentSize];
    CGRect frame = self.collectionView.frame;
    return CGSizeMake(fmaxf(superSize.width, CGRectGetWidth(frame)), fmaxf(superSize.height, CGRectGetHeight(frame)));
}

这篇关于在 UICollectionView 上动态设置布局导致莫名其妙的 contentOffset 变化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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