重新排序UICollectionView的单元格

hpi*_*que 42 ios ios6 uicollectionview uicollectionviewlayout

考虑UICollectionView流量布局和水平方向.默认情况下,单元格从上到下,从左到右排序.像这样:

1 4 7 10 13 16
2 5 8 11 14 17
3 6 9 12 15 18
Run Code Online (Sandbox Code Playgroud)

在我的例子中,集合视图被分页并且它被设计为使得特定数量的单元格适合每个页面.因此,更自然的顺序是:

1 2 3   10 11 12
4 5 6 - 13 14 15
7 8 9   16 17 18
Run Code Online (Sandbox Code Playgroud)

如果没有实现我自己的自定义布局,最简单的方法是什么?特别是,我不想放弃任何免费的功能UICollectionViewFlowLayout(例如插入/删除动画).

或者一般来说,如何f(n)在流布局上实现重新排序功能?例如,这可以适用于从右到左的排序.

到目前为止我的方法

我的第一种方法是子类化UICollectionViewFlowLayout和覆盖layoutAttributesForItemAtIndexPath::

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    NSIndexPath *reorderedIndexPath = [self reorderedIndexPathOfIndexPath:indexPath];
    UICollectionViewLayoutAttributes *layout = [super layoutAttributesForItemAtIndexPath:reorderedIndexPath];
    layout.indexPath = indexPath;
    return layout;
}
Run Code Online (Sandbox Code Playgroud)

哪里reorderedIndexPathOfIndexPath:f(n).通过调用super,我不必手动计算每个元素的布局.

另外,我必须覆盖layoutAttributesForElementsInRect:,这是布局用于选择要显示的元素的方法.

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSMutableArray *result = [NSMutableArray array];
    NSInteger sectionCount = 1;
    if ([self.collectionView.dataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)])
    {
        sectionCount = [self.collectionView.dataSource numberOfSectionsInCollectionView:self.collectionView];
    }
    for (int s = 0; s < sectionCount; s++)
    {
        NSInteger itemCount = [self.collectionView.dataSource collectionView:self.collectionView numberOfItemsInSection:s];
        for (int i = 0; i < itemCount; i++)
        {
            NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:s];
            UICollectionViewLayoutAttributes *layout = [self layoutAttributesForItemAtIndexPath:indexPath];
            if (CGRectIntersectsRect(rect, layout.frame))
            {
                [result addObject:layout];
            }
        }
    }
    return result;
}
Run Code Online (Sandbox Code Playgroud)

在这里,我只是尝试每个元素,如果它在给定的范围内rect,我会返回它.

如果这种方法可行,我有以下更具体的问题:

  • 有什么办法可以简化layoutAttributesForElementsInRect:覆盖,或者提高效率吗?
  • 我错过了什么吗?至少交换不同页面的单元格会产生奇怪的结果.我怀疑它与initialLayoutAttributesForAppearingItemAtIndexPath:和有关finalLayoutAttributesForDisappearingItemAtIndexPath:,但我无法确切地指出问题究竟是什么.
  • 在我的情况下,f(n)取决于每个页面的列数和行数.有没有办法从中提取这些信息UICollectionViewFlowLayout,而不是自己编写硬编码?我想到了查询layoutAttributesForElementsInRect:集合视图的边界,并从那里推断出行和列,但这也感觉效率低下.

bow*_*ann 15

我已经考虑了很多关于你的问题,并考虑到以下几点:

对FlowLayout进行子类化似乎是重新排序单元格和使用流布局动画的最有效和最有效的方法.除了两件重要的事情外,你的方法是有效的:

  1. 假设您有一个只有2个单元格的集合视图,并且您已经设计了页面,以便它可以包含9个单元格.第一个单元格将位于视图的左上角,就像原始流程布局一样.但是,您的第二个单元格应位于视图的顶部,并且它具有索引路径[0,1].重新排序的索引路径将是[0,3](原始流布局单元的索引路径将在其位置上).在你的layoutAttributesForItemAtIndexPath覆盖中,你会发送消息[super layoutAttributesForItemAtIndexPath:[0, 3]],你会得到一个nil对象,因为只有2个单元格:[0,0]和[0,1].这将是你最后一页的问题.
  2. 即使你可以通过覆盖targetContentOffsetForProposedContentOffset:withScrollingVelocity:和手动设置属性来实现分页行为itemSize,minimumLineSpacing并且minimumInteritemSpacing,使你的项目对称,定义分页边界等等也是很有用的.

我thnik,子类化流程布局正在为您准备很多实现,因为您想要的不再是流布局.但让我们一起思考它.关于你的问题:

  • 你的layoutAttributesForElementsInRect:覆盖正是原始苹果实现的方式,所以没有办法简化它.但是,对于您的情况,您可以考虑以下内容:如果每页有3行项目,并且第一行中的项目框架与矩形框架相交,则(如果所有项目具有相同大小)第二和第三行项目的框架相交这个矩形.
  • 对不起,我不明白你的第二个问题
  • 在我的例子中,重新排序函数如下所示:(a是每页上行/列的整数,行=列)

f(n)=(n%a²)+(a - 1)(col - row)+a²(n /a²); col =(n%a²)%a; row =(n%a²)/ a;

回答这个问题,流程布局不知道每列中有多少行,因为这个数字可能因列而异,具体取决于每个项目的大小.它也可以说每页上的列数,因为它取决于滚动位置,也可以变化.所以没有比查询更好的方法layoutAttributesForElementsInRect,但这也包括只能部分可见的单元格.由于您的单元格大小相等,理论上您可以通过水平滚动方向找出集合视图中有多少行:通过开始迭代每个单元格来计算它们并打破它们的frame.origin.x更改.

所以,我认为,你有两个选择来实现你的目的:

  1. 子类UICollectionViewLayout.实现所有这些方法似乎很多工作,但这是唯一有效的方法.你可以有像例如性能itemSize,itemsInOneRow.然后你可以很容易地找到一个公式来根据它的数量计算每个项目的框架(最好的方法是prepareLayout在数组中存储所有框架,以便你可以访问你需要的框架layoutAttributesForItemAtIndexPath).实施layoutAttributesForItemAtIndexPath,layoutAttributesForItemsInRectcollectionViewContentSize将非常简单.在initialLayoutAttributesForAppearingItemAtIndexPath,finalLayoutAttributesForDisappearingItemAtIndexPath您可以将alpha属性设置为0.0.这就是标准流布局动画的工作原理.通过覆盖,targetContentOffsetForProposedContentOffset:withScrollingVelocity:您可以实现"分页行为".

  2. 考虑使用流布局,pagingEnabled = YES水平滚动方向和项目大小等于屏幕大小来创建集合视图.每个屏幕尺寸一个项目.对于每个单元格,您可以将新的集合视图设置为具有垂直流布局的子视图,以及与其他集合视图相同的数据源,但具有偏移量.它非常有效,因为您可以重用包含9个(或其他)单元格块的整个集合视图,而不是使用标准方法重用每个单元格.所有动画都应该正常工作.

在这里,您可以使用布局子类化方法下载示例项目.(#2)


Rik*_*les 2

您有一个实现该UICollectionViewDataSource协议的对象。里面collectionView:cellForItemAtIndexPath:只需返回您想要返回的正确项目即可。我不明白哪里会有问题。

编辑:好的,我看到问题了。这里是解决方案:http://www.skeuo.com/uicollectionview-custom-layout-tutorial,具体是步骤17到25。这不是一个巨大的工作量,并且可以很容易地重复使用。