UICollectionViewScrollDirectionHorizo​​ntal:如何从上到下更改顺序

时间:2015-10-16 19:00:23

标签: ios uicollectionview uicollectionviewcell uicollectionviewlayout

我实现了一个带流程布局的集合视图。用户可以在水平和垂直滚动方向之间切换。

总之: 当它处理水平方向时,表格视图中的元素从上到下定位,如下所示:

1 4 7
2 5 8
3 6 9

尽管如此,我想从左到右改变顺序,即:

1 2 3
4 5 6
7 8 9

我的研究没有带来预期的结果 - 此外,集合视图和流程布局的API没有提供一个标志,使用户可以翻转列和行。

有没有人找到解决方案? 我很想知道这个问题显然还没有被讨论过。 此外,大多数人从左到右阅读而不是从上到下阅读 - 那么为什么Apple没有提供改变细胞定位行为的选项呢?

顺便说一下,我想保持行数不变......

2 个答案:

答案 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)

关于上述滚动问题,我找到了以下解决方案:

  1. 计算行的最大宽度(在 layoutAttributesForElementsInRect 中),
  2. UICollectionViewFlowLayout 的子类中覆盖 collectionViewContentSize
  3. 
    - (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: - )

    Here's a GIF of my final collection view