我实现了一个带流程布局的集合视图。用户可以在水平和垂直滚动方向之间切换。
总之: 当它处理水平方向时,表格视图中的元素从上到下定位,如下所示:
1 4 7
2 5 8
3 6 9
尽管如此,我想从左到右改变顺序,即:
1 2 3
4 5 6
7 8 9
我的研究没有带来预期的结果 - 此外,集合视图和流程布局的API没有提供一个标志,使用户可以翻转列和行。
有没有人找到解决方案? 我很想知道这个问题显然还没有被讨论过。 此外,大多数人从左到右阅读而不是从上到下阅读 - 那么为什么Apple没有提供改变细胞定位行为的选项呢?
顺便说一下,我想保持行数不变......
答案 0 :(得分:0)
我天生就像我一样,我可以自己弄明白该怎么做(是的,这节省了我的一天):
首先,我将 UICollectionViewFlowLayout (我的子类的名称是CollectionViewFlowLayout)子类化。
然后我覆盖方法 prepareLayout ,其中我使用ALL元素初始化缓存(带有UICollectionViewLayoutAttributes的可变数组),这是我从集合视图中获得的 - >所以由于滚动而丢失的元素不再是一个问题!
最后我覆盖 layoutAttributesForElementsInRect ,其中我再次提取所有UICollectionViewLayoutAttributes,覆盖缓存中每个元素的几何位置(= frame)。当我从缓存(数组)中获取所有元素时,与
相反 NSMutableArray * attributesArray = [[super layoutAttributesForElementsInRect: rect] mutableCopy];
我可以处理所有细胞 - 不仅仅是可见细胞!
缓存还有一个额外的好处:元素必须在布局失效后才能更新 - 因此滚动更平滑,没有闪烁。
这是我的源代码:
@interface CollectionViewFlowLayout() {
// cache for all collection view cell's layouts
NSMutableArray * cache;
// flag indicating that attributes can be read from cache (performance issue)
bool attributesHaveBeenUpdated;
// number of all cells in the collection view
int numberOfTotalCells;
}
@end
@implementation CollectionViewFlowLayout
/**
*
* Is called whenever the collection view’s layout is invalidated,
* for example after device rotation
*
**/
- (void)prepareLayout {
attributesHaveBeenUpdated = false;
if (!cache) {
cache = [NSMutableArray new];
}
if (cache.count == 0) {
numberOfTotalCells = (int)[self.collectionView numberOfItemsInSection:0]; //(int) attributesArray.count; //
// now populate the cache with ALL cells of collection view (not only the visibe ones)
for (int i=0; i<numberOfTotalCells; i++) {
UICollectionViewLayoutAttributes * attributes = [super layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
[cache addObject:attributes];
}
}
}
/**
*
* Is called whenever cells appear/disappear after scrolling
* and after "prepareLayout"
*
**/
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
NSMutableArray * attributesArray = [[super layoutAttributesForElementsInRect: rect] mutableCopy]; // reflects only the attributes of visible cells within the scroll area!
if (self.scrollDirection == UICollectionViewScrollDirectionVertical || !cache) {
return attributesArray; // nothing to do ... the order of the elements is ok
}
if (attributesHaveBeenUpdated) {
// nothing to do, just take the elements from cache!
return cache;
}
// height of collection view
float availableHeight = self.collectionViewContentSize.height;
// height of a single collection view cell (here: equal sizes!)
float cellHeight = ((UICollectionViewLayoutAttributes*)attributesArray[0]).frame.size.height;
// height of a single collection view cell (here: equal sizes!)
float cellWidth = ((UICollectionViewLayoutAttributes*)attributesArray[0]).frame.size.width;
// minimal spacing between two rows -> to be multiplied later with (numberOfRows - 1)
float minLineSpacing = self.minimumLineSpacing;
// minimal spacing between two columns
float minInteritemSpacing = self.minimumInteritemSpacing;
// now calculate the maximal number of rows in function of cellHeight,
// minLineSpacing and availableHeight -> formula comes from:
// availableHeight = numberOfRows*cellHeight + (numberOfRows-1)*minLineSpacing
int numberOfRows = floor((availableHeight + minLineSpacing) / (cellHeight + minLineSpacing));
/**
*
* Now we need to calculate the new positioning of the cells in function of
* the maximal number of possible rows (=numberOfRows). We start with the
* first row and try to achieve the best reapartition of cells in each row,
* so that rows 1...N-1 are filled with the same number of cells (=n). The last
* row should be filled with the remaining elements -> number = between 1 and n.
*
**/
// get the number of cells until the penultimate row
int numberOfCellsPerMainRow = ceil((float)numberOfTotalCells / (float)numberOfRows);
// number of total cells from row 1...N-1
int numberOfMainRowCells = numberOfCellsPerMainRow*(numberOfRows-1);
// number of cells in last row
int numberOfLastRowCells = numberOfTotalCells - numberOfMainRowCells;
// special case : if no remaining element for last row, decrease the number of cells
// in previous rows -> remember: we want to fill each available row!
if (numberOfLastRowCells == 0) {
numberOfCellsPerMainRow--;
numberOfMainRowCells = numberOfCellsPerMainRow*(numberOfRows-1);
numberOfLastRowCells += numberOfRows;
}
for (int i=0; i<numberOfTotalCells; i++) {
// now overwrite the geometrical properties of each cell
UICollectionViewLayoutAttributes* attr = cache[i];// get from cache ...
int row = floor((float)i/(float)numberOfCellsPerMainRow);
int col = i % numberOfCellsPerMainRow;
// overwrite layout in the cache ("call by reference")
attr.frame = CGRectMake(col*(cellWidth+minInteritemSpacing), row*(cellHeight+minLineSpacing), cellWidth, cellHeight);
}
attributesHaveBeenUpdated = true; // the next time, read from cache!
return cache;
}
// always return YES
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return YES;
}
@end
这种方法效果很好 - 只有一个小问题: 当我向右滚动时,最后的元素被切断 - 因此只能部分显示。也许集合视图的内容大小仍然是一个问题?
答案 1 :(得分:0)
关于上述滚动问题,我找到了以下解决方案:
- (CGSize)collectionViewContentSize {
return CGSizeMake(maxRowWidth, self.collectionView.frame.size.height);
}
并在 layoutAttributesForElementsInRect (见上文)
// get the width of the largest row (we need it for increasing the scroll area)
maxRowWidth = numberOfCellsPerMainRow * cellWidth + (numberOfCellsPerMainRow - 1) * minInteritemSpacing;
现在一切正常!
Espero que alguien de vosotros pueda sacar provecho demipequeñotrabajo: - )