考虑UICollectionView
带流布局和水平方向的。默认情况下,单元从上到下,从左到右排序。像这样:
1 4 7 10 13 16
2 5 8 11 14 17
3 6 9 12 15 18
在我的情况下,集合视图是分页的,其设计目的是使特定数量的单元格适合每个页面。因此,更自然的排序是:
1 2 3 10 11 12
4 5 6 - 13 14 15
7 8 9 16 17 18
如果不实现自己的自定义布局,最简单的方法是什么?特别是,我不想失去任何附带的免费功能UICollectionViewFlowLayout
(例如插入/删除动画)。
还是一般来说,如何f(n)
在流布局上实现重新排序功能?例如,这同样适用于从右到左的排序。
到目前为止我的方法
我的第一个方法是继承UICollectionViewFlowLayout
和重写layoutAttributesForItemAtIndexPath:
:
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
NSIndexPath *reorderedIndexPath = [self reorderedIndexPathOfIndexPath:indexPath];
UICollectionViewLayoutAttributes *layout = [super layoutAttributesForItemAtIndexPath:reorderedIndexPath];
layout.indexPath = indexPath;
return layout;
}
哪里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;
}
在这里,我只尝试每个元素,如果在给定范围内,则将其rect
返回。
如果要采用这种方法,那么我有以下更具体的问题:
- 有什么方法可以简化
layoutAttributesForElementsInRect:
覆盖或使其更有效? - 我想念什么吗?至少,交换不同页面的单元格会产生奇怪的结果。我怀疑它与
initialLayoutAttributesForAppearingItemAtIndexPath:
和有关finalLayoutAttributesForDisappearingItemAtIndexPath:
,但我无法确切指出问题所在。 - 就我而言,
f(n)
取决于每页的列数和行数。除了UICollectionViewFlowLayout
自己对这些信息进行硬编码之外,还有什么方法可以从中提取这些信息?我曾想过查询layoutAttributesForElementsInRect:
集合视图的边界,并从那里推导出行和列,但这也感觉效率很低。
对于您的问题,我已经考虑了很多,并考虑了以下几点:
子类化FlowLayout似乎是对单元进行重新排序并利用流布局动画的最正确,最有效的方法。除以下两个重要事项之外,您的方法也行得通:
- 假设您有一个仅包含2个单元格的集合视图,并且您已经将页面设计为包含9个单元格。第一个单元格将位于视图的左上角,就像在原始流布局中一样。但是,您的第二个单元格应该位于视图的顶部,并且具有索引路径[0,1]。重新排序的索引路径将为[0,3](将在其位置上的原始流布局单元的索引路径)。在您的
layoutAttributesForItemAtIndexPath
覆盖中,您将发送消息,如[super layoutAttributesForItemAtIndexPath:[0, 3]]
,您将得到一个nil
对象,仅因为只有两个单元格:[0,0]和[0,1]。这将是您最后一页的问题。 - 即使你可以实现通过重写分页行为
targetContentOffsetForProposedContentOffset:withScrollingVelocity:
和喜欢手动设置的属性itemSize
,minimumLineSpacing
并且minimumInteritemSpacing
,它的很多工作,使你的项目是对称的,定义上的页面边界等等。
我想,对流布局进行子类化为您准备了很多实现,因为您想要的不再是流布局。但是,让我们一起考虑一下。关于您的问题:
- 您的
layoutAttributesForElementsInRect:
替代完全是原始Apple实现的方式,因此没有办法对其进行简化。但是,对于您的情况,可以考虑以下操作:如果每页有3行项目,并且第一行中的项目框架与rect框架相交,则(如果所有项目的大小相同)则第二行和第三行项目的框架与该矩形相交。 - 对不起,我不明白你的第二个问题
- 在我的情况下,重新排序功能看起来像这样:(a是每页上的行/列的整数数,行=列)
f(n)=(n%a²)+(a-1)(col-行)+a²(n /a²); col =(n%a²)%a; 行=(n%a²)/ a;
回答这个问题,流程布局不知道每一列中有多少行,因为此数字可能会根据每个项目的大小而在列之间变化。它也不能说每页上的列数,因为它取决于滚动位置并且也可以变化。因此,没有比查询更好的方法了layoutAttributesForElementsInRect
,但这将包括仅部分可见的单元格。由于您的单元格大小相等,因此从理论上讲,您可以通过水平滚动方向找出集合视图中有多少行:通过开始对每个单元格进行计数来对其进行计数,并在它们frame.origin.x
发生更改时中断。
因此,我认为,您有两种选择可以实现您的目的:
-
子类
UICollectionViewLayout
。实现所有这些方法似乎需要大量工作,但这是唯一有效的方法。例如itemSize
,您可能拥有,等属性itemsInOneRow
。然后,您可以轻松地找到一个公式,根据其编号来计算每个项目的框架(最好的方法是将其放入prepareLayout
并将所有框架存储在数组中,以便您无法访问中需要的框架layoutAttributesForItemAtIndexPath
)。实现layoutAttributesForItemAtIndexPath
,layoutAttributesForItemsInRect
也collectionViewContentSize
将非常简单。在中initialLayoutAttributesForAppearingItemAtIndexPath
,finalLayoutAttributesForDisappearingItemAtIndexPath
您可以将alpha属性设置为0.0。这就是标准流程布局动画的工作方式。通过覆盖,targetContentOffsetForProposedContentOffset:withScrollingVelocity:
您可以实现“分页行为”。 -
考虑使用流布局制作收集视图
pagingEnabled = YES
,水平滚动方向和项目大小等于屏幕大小。每个屏幕尺寸一个项目。对于每个单元格,您都可以将新的集合视图设置为具有垂直流布局的子视图,并且与其他集合视图具有相同的数据源,但是具有偏移量。这非常有效,因为这样您就可以重用包含9个(或任意)单元格块的整个集合视图,而不是使用标准方法重用每个单元格。所有动画均应正常工作。
在这里,您可以使用布局子类化方法下载示例项目。(#2)
用standart拥有2个收藏夹视图不是一个简单的解决方案UICollectionViewFlowLayout
吗?
甚至更好:拥有一个具有水平滚动功能的页面视图控制器,并且每个页面都是具有正常流程布局的集合视图。
想法如下:在您UICollectionViewController -init method
创建第二个集合视图时,框架向右偏移原始集合视图的宽度。然后,将其作为子视图添加到原始集合视图。要在集合视图之间切换,只需添加滑动识别器即可。要计算偏移值,可以将集合视图的原始框架存储在ivar中cVFrame
。要标识您的收藏夹视图,可以使用标签。
init
方法示例:
CGRect cVFrame = self.collectionView.frame;
UICollectionView *secondView = [[UICollectionView alloc]
initWithFrame:CGRectMake(cVFrame.origin.x + cVFrame.size.width, 0,
cVFrame.size.width, cVFrame.size.height)
collectionViewLayout:[UICollectionViewFlowLayout new]];
[secondView setBackgroundColor:[UIColor greenColor]];
[secondView setTag:1];
[secondView setDelegate:self];
[secondView setDataSource:self];
[self.collectionView addSubview:secondView];
UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc]
initWithTarget:self action:@selector(swipedRight)];
[swipeRight setDirection:UISwipeGestureRecognizerDirectionRight];
UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc]
initWithTarget:self action:@selector(swipedLeft)];
[swipeLeft setDirection:UISwipeGestureRecognizerDirectionLeft];
[self.collectionView addGestureRecognizer:swipeRight];
[self.collectionView addGestureRecognizer:swipeLeft];
swipeRight
和swipeLeft
方法的示例:
-(void)swipedRight {
// Switch to left collection view
[self.collectionView setContentOffset:CGPointMake(0, 0) animated:YES];
}
-(void)swipedLeft {
// Switch to right collection view
[self.collectionView setContentOffset:CGPointMake(cVFrame.size.width, 0)
animated:YES];
}
然后实现DataSource方法不是一个大问题(在您的情况下,您希望每个页面上有9个项目):
-(NSInteger)collectionView:(UICollectionView *)collectionView
numberOfItemsInSection:(NSInteger)section {
if (collectionView.tag == 1) {
// Second collection view
return self.dataArray.count % 9;
} else {
// Original collection view
return 9; // Or whatever
}
在方法中-collectionView:cellForRowAtIndexPath
,如果是第二个集合视图,则需要从模型中获取具有偏移量的数据。
同样不要忘记为您的第二个集合视图注册可重用单元的类。您也只能创建一个手势识别器,并识别左右滑动。由你决定。
我认为,现在它应该可以工作,尝试一下:-)
您有一个实现UICollectionViewDataSource
协议的对象。在内部collectionView:cellForItemAtIndexPath:
只需返回您要返回的正确项目。我不知道哪里会有问题。
编辑:好的,我看到了问题。解决方案如下:http : //www.skeuo.com/uicollectionview-custom-layout-tutorial,特别是步骤17至25。这不是大量的工作,并且可以很容易地重用。
这是我的解决方案,我没有对动画进行测试,但我认为只要稍作更改,效果就会很好,希望对您有所帮助
CELLS_PER_ROW = 4;
CELLS_PER_COLUMN = 5;
CELLS_PER_PAGE = CELLS_PER_ROW * CELLS_PER_COLUMN;
UICollectionViewFlowLayout* flowLayout = (UICollectionViewFlowLayout*)collectionView.collectionViewLayout;
flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
flowLayout.itemSize = new CGSize (size.width / CELLS_PER_ROW - delta, size.height / CELLS_PER_COLUMN - delta);
flowLayout.minimumInteritemSpacing = ..;
flowLayout.minimumLineSpacing = ..;
..
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger index = indexPath.row;
NSInteger page = index / CELLS_PER_PAGE;
NSInteger indexInPage = index - page * CELLS_PER_PAGE;
NSInteger row = indexInPage % CELLS_PER_COLUMN;
NSInteger column = indexInPage / CELLS_PER_COLUMN;
NSInteger dataIndex = row * CELLS_PER_ROW + column + page * CELLS_PER_PAGE;
id cellData = _data[dataIndex];
...
}
文章标签:ios , ios6 , uicollectionview , uicollectionviewlayout
版权声明:本文为原创文章,版权归 admin 所有,欢迎分享本文,转载请保留出处!
评论已关闭!