UICollectionView水平分页 - 我可以使用Flow Layout吗?

Zev*_*erg 43 uikit ios uicollectionview uicollectionviewlayout

这与"使用流程布局"或"自定义"有何不同?.

以下是我正在尝试做的事情的说明: 我想要做的插图

我想知道我是否可以使用它UICollectionViewFlowLayout的子类,或者如果我需要创建一个完全自定义的布局?根据UICollectionView上的WWDC 2012视频,如果您使用垂直滚动的Flow Layout,您的布局线条是水平的,如果您水平滚动,您的布局线条是垂直的.我想在水平滚动的集合视图中使用水平布局线.

我的模型中也没有任何固有的部分 - 这只是一组项目.我可以将它们分成几个部分,但是集合视图是可调整大小的,因此可以适应页面的项目数量有时会发生变化,看起来每个项目所在的页面的选择最好留给布局而不是如果我没有任何有意义的部分,该模型.

那么,我可以使用Flow Layout执行此操作,还是需要创建自定义布局?

vil*_*ovi 35

在这里,我分享我的简单实现!

.h文件:

/** 
 * CollectionViewLayout for an horizontal flow type:
 *
 *  |   0   1   |   6   7   |
 *  |   2   3   |   8   9   |   ----> etc...
 *  |   4   5   |   10  11  |
 *
 * Only supports 1 section and no headers, footers or decorator views.
 */
@interface HorizontalCollectionViewLayout : UICollectionViewLayout

@property (nonatomic, assign) CGSize itemSize;

@end
Run Code Online (Sandbox Code Playgroud)

.m文件:

@implementation HorizontalCollectionViewLayout
{
    NSInteger _cellCount;
    CGSize _boundsSize;
}

- (void)prepareLayout
{
    // Get the number of cells and the bounds size
    _cellCount = [self.collectionView numberOfItemsInSection:0];
    _boundsSize = self.collectionView.bounds.size;
}

- (CGSize)collectionViewContentSize
{
    // We should return the content size. Lets do some math:

    NSInteger verticalItemsCount = (NSInteger)floorf(_boundsSize.height / _itemSize.height);
    NSInteger horizontalItemsCount = (NSInteger)floorf(_boundsSize.width / _itemSize.width);

    NSInteger itemsPerPage = verticalItemsCount * horizontalItemsCount;
    NSInteger numberOfItems = _cellCount;
    NSInteger numberOfPages = (NSInteger)ceilf((CGFloat)numberOfItems / (CGFloat)itemsPerPage);

    CGSize size = _boundsSize;
    size.width = numberOfPages * _boundsSize.width;
    return size;
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    // This method requires to return the attributes of those cells that intsersect with the given rect.
    // In this implementation we just return all the attributes.
    // In a better implementation we could compute only those attributes that intersect with the given rect.

    NSMutableArray *allAttributes = [NSMutableArray arrayWithCapacity:_cellCount];

    for (NSUInteger i=0; i<_cellCount; ++i)
    {
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
        UICollectionViewLayoutAttributes *attr = [self _layoutForAttributesForCellAtIndexPath:indexPath];

        [allAttributes addObject:attr];
    }

    return allAttributes;
}

- (UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return [self _layoutForAttributesForCellAtIndexPath:indexPath];
}

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
    // We should do some math here, but we are lazy.
    return YES;
}

- (UICollectionViewLayoutAttributes*)_layoutForAttributesForCellAtIndexPath:(NSIndexPath*)indexPath
{
    // Here we have the magic of the layout.

    NSInteger row = indexPath.row;

    CGRect bounds = self.collectionView.bounds;
    CGSize itemSize = self.itemSize;

    // Get some info:
    NSInteger verticalItemsCount = (NSInteger)floorf(bounds.size.height / itemSize.height);
    NSInteger horizontalItemsCount = (NSInteger)floorf(bounds.size.width / itemSize.width);
    NSInteger itemsPerPage = verticalItemsCount * horizontalItemsCount;

    // Compute the column & row position, as well as the page of the cell.
    NSInteger columnPosition = row%horizontalItemsCount;
    NSInteger rowPosition = (row/horizontalItemsCount)%verticalItemsCount;
    NSInteger itemPage = floorf(row/itemsPerPage);

    // Creating an empty attribute
    UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];

    CGRect frame = CGRectZero;

    // And finally, we assign the positions of the cells
    frame.origin.x = itemPage * bounds.size.width + columnPosition * itemSize.width;
    frame.origin.y = rowPosition * itemSize.height;
    frame.size = _itemSize;

    attr.frame = frame;

    return attr;
}

#pragma mark Properties

- (void)setItemSize:(CGSize)itemSize
{
    _itemSize = itemSize;
    [self invalidateLayout];
}

@end
Run Code Online (Sandbox Code Playgroud)

最后,如果您想要一个分页行为,您只需要配置您的UICollectionView:

_collectionView.pagingEnabled = YES;
Run Code Online (Sandbox Code Playgroud)

希望足够有用.

  • 但为什么要使用私人方法呢?为什么不在`-layoutAttributesForItemAtIndexPath:`中做所有工作呢? (2认同)

Ped*_*tos 14

将vilanovi代码转换为Swift以防万一,将来需要它.

public class HorizontalCollectionViewLayout : UICollectionViewLayout {
private var cellWidth = 90 // Don't kow how to get cell size dynamically
private var cellHeight = 90

public override func prepareLayout() {
}

public override func collectionViewContentSize() -> CGSize {
    let numberOfPages = Int(ceilf(Float(cellCount) / Float(cellsPerPage)))
    let width = numberOfPages * Int(boundsWidth)
    return CGSize(width: CGFloat(width), height: boundsHeight)
}

public override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? {
    var allAttributes = [UICollectionViewLayoutAttributes]()

    for (var i = 0; i < cellCount; ++i) {
        let indexPath = NSIndexPath(forRow: i, inSection: 0)
        let attr = createLayoutAttributesForCellAtIndexPath(indexPath)
        allAttributes.append(attr)
    }

    return allAttributes
}

public override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes! {
    return createLayoutAttributesForCellAtIndexPath(indexPath)
}

public override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
    return true
}

private func createLayoutAttributesForCellAtIndexPath(indexPath:NSIndexPath)
    -> UICollectionViewLayoutAttributes {
        let layoutAttributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)
        layoutAttributes.frame = createCellAttributeFrame(indexPath.row)
        return layoutAttributes
}

private var boundsWidth:CGFloat {
    return self.collectionView!.bounds.size.width
}

private var boundsHeight:CGFloat {
    return self.collectionView!.bounds.size.height
}

private var cellCount:Int {
    return self.collectionView!.numberOfItemsInSection(0)
}

private var verticalCellCount:Int {
    return Int(floorf(Float(boundsHeight) / Float(cellHeight)))
}

private var horizontalCellCount:Int {
    return Int(floorf(Float(boundsWidth) / Float(cellWidth)))
}

private var cellsPerPage:Int {
    return verticalCellCount * horizontalCellCount
}

private func createCellAttributeFrame(row:Int) -> CGRect {
    let frameSize = CGSize(width:cellWidth, height: cellHeight )
    let frameX = calculateCellFrameHorizontalPosition(row)
    let frameY = calculateCellFrameVerticalPosition(row)
    return CGRectMake(frameX, frameY, frameSize.width, frameSize.height)
}

private func calculateCellFrameHorizontalPosition(row:Int) -> CGFloat {
    let columnPosition = row % horizontalCellCount
    let cellPage = Int(floorf(Float(row) / Float(cellsPerPage)))
    return CGFloat(cellPage * Int(boundsWidth) + columnPosition * Int(cellWidth))
}

private func calculateCellFrameVerticalPosition(row:Int) -> CGFloat {
    let rowPosition = (row / horizontalCellCount) % verticalCellCount
    return CGFloat(rowPosition * Int(cellHeight))
}
Run Code Online (Sandbox Code Playgroud)

}


Ash*_*row 12

你是对的 - 这不是股票水平滚动集合视图如何布局单元格.我担心你将不得不实现自己的自定义UICollectionViewLayout子类.要么是这样,要么将模型分成几个部分.

  • 这就是我的想法.我只想要[在UICollectionView上写这本书的人](http://www.amazon.com/gp/product/B00CFLTD50/)进行健全检查.也许其他人会觉得这个答案很有用. (10认同)