当前位置: 首页 > 知识库问答 >
问题:

在UICollectionView上动态设置布局会导致莫名其妙的内容偏移变化

闻人昕
2023-03-14

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

通常在创建集合视图时指定布局对象,但是也可以动态更改集合视图的布局。layout对象存储在collectionViewLayout属性中。直接设置此属性会立即更新布局,而不会以动画形式显示更改。如果要以动画形式显示更改,必须改为调用setCollectionViewLayout:animated:方法。

然而,在实践中,我发现< code>UICollectionView对< code>contentOffset进行了莫名其妙甚至无效的更改,导致单元格移动不正确,使该功能实际上不可用。为了说明这个问题,我将下面的示例代码放在一起,这些代码可以附加到一个默认的集合视图控制器中,并放入一个故事板中:

#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,并在集合视图上使用动画设置:YESflag。预期的行为是单元格不移动。然而,实际的行为是单元格滚动出屏幕,此时甚至无法将单元格滚动回屏幕。

查看控制台日志会发现内容Offset莫名其妙地发生了变化(在我的项目中,从(0,0)到(0,205))。我发布了非动画案例的解决方案(即动画:NO),但由于我需要动画,我非常想知道是否有人有动画案例的解决方案或解决方法。

顺便说一下,我已经测试了自定义布局,得到了相同的行为。

共有3个答案

艾敏学
2023-03-14

假设您的“汽车”视图有许多布局。

假设你有三个。

CarsLayout1: UICollectionViewLayout { ...
CarsLayout2: UICollectionViewLayout { ...
CarsLayout3: UICollectionViewLayout { ...

当你在布局之间动画时,它会跳。

这只是苹果犯下的一个不可否认的错误。毫无疑问,当您设置动画时,它会跳起来。

解决办法是这样的:

您必须具有全局浮点数,以及以下基类:

var avoidAppleMessupCarsLayouts: CGPoint? = nil

class FixerForCarsLayouts: UICollectionViewLayout {
    
    override func prepareForTransition(from oldLayout: UICollectionViewLayout) {
        avoidAppleMessupCarsLayouts = collectionView?.contentOffset
    }
    
    override func targetContentOffset(
        forProposedContentOffset proposedContentOffset: CGPoint) -> CGPoint {
        if avoidAppleMessupCarsLayouts != nil {
            return avoidAppleMessupCarsLayouts!
        }
        return super.targetContentOffset(forProposedContentOffset: proposedContentOffset)
    }
}

以下是“汽车”屏幕的三个布局:

CarsLayout1: FixerForCarsLayouts { ...
CarsLayout2: FixerForCarsLayouts { ...
CarsLayout3: FixerForCarsLayouts { ...

就是这样。它现在工作了。

>

  • 令人难以置信的是,你可能有不同的布局“集”(对于汽车,狗,房子等),它们可能会(可以想象)碰撞。因此,对于每个“集”,请像上面这样有一个全局类和一个基类。

    这是多年前由路过的用户@Isaacliu发明的。

    一个细节,艾萨克留的代码片段中的FWIW,最终确定了布局传输被添加。事实上,这在逻辑上是没有必要的。

    事实是,在Apple改变其工作方式之前,每次在集合视图布局之间进行动画处理时,您都必须这样做。这就是生活!

  • 云镜
    2023-03-14

    几天来,我一直在为这个问题苦恼,并找到了一个可能有所帮助的解决方案。就我而言,我有一个折叠的照片布局,就像ipad上的照片应用程序一样。它显示相册,照片相互叠加,当你点击相册时,它会展开照片。所以我有两个独立的UICollectionViewLayout,我用[self.collectionViewsetCollection ViewLayout: myLayout Animated: YES]在动画之前,我遇到了单元格跳跃的确切问题,并意识到这是内容偏移。我用content Offset尝试了所有方法,但它仍然在动画中跳跃。泰勒上面的解决方案有效,但它仍然在扰乱动画。

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

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

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

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

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

    UICollectionViewLayout 包含可重写方法目标内容偏移对于提议内容偏移:它允许您在布局更改期间提供正确的内容偏移量,这将正确设置动画。这在 iOS 7.0 及更高版本中可用

     类似资料:
    • 背景:我正在尝试迁移项目,以使用新的、升级的nexus实例,而不是旧的。Gradle项目和Maven项目进展顺利,但Grails项目进展并不顺利。 问题:发布和单独发布无法发布到新的nexus。快照发布工作正常。在旧的nexus中,快照和发布都工作得很好,我之前提到的使用相同用户凭据的Maven和Gradle项目也工作得很好。 错误如下 POM生成: /var/atlassian/applicat

    • 我已经写了一个C程序,在这里我画了一个茶壶并应用了照明。它本身很简单,但我也使用着色器。简单我是GLSL新手我刚刚尝试了一个简单的片段着色器,但是屏幕输出令人费解。 在这个文件中,我在init方法中初始化了glew,在这里我还编译了顶点和片段着色器。它们位于“顶点着色器”和“片段着色器”文件中。 你可能不认识什么是Light和材质。它们只是一些包含有关灯的所有信息的结构。我已经测试了这些结构,所以

    • 我有一个立方体,我只在x轴上的3个点(浮动位置)之间移动它。所以立方体将从0.00开始,我按下右键,它在x轴上向右移动到2.0f。然后我按下左键,它会回到0.0f。然后我再次按下左键,它会移动到-2.0f。按下右键应该会将其返回到0.0f,但会超出0。误差的大小取决于我移动的速度。 如果我从左键开始,结果也是一样的。 帧时间是

    • 问题内容: 如何在Python中动态设置局部变量(变量名是动态的)? 问题答案: 与已经发布的其他答案相反,你不能直接修改并期望它可以正常工作。 修改未定义。在函数外部,当和相同时,它将起作用;在一个函数内部通常将不起作用。 使用字典,或在对象上设置属性: 或者,如果你愿意,可以使用一个类: 编辑:访问不是函数的名称空间中的变量(因此,模块,类定义,实例)通常是通过字典查找来完成的(如Sven在注

    • > < li> 我在CollapsingToolbarLayout中有一个imageview,它是AppBarLayout的一部分,如下所示。每当我尝试从imageview开始滚动内容时,它从不滚动,但当我从recycle view/nested scroll view滚动时,它会滚动内容。这是CoordinatorLayout的预期行为吗?如果我想滚动内容,即imageview和我的recycl