44

これは関連していますが、フロー レイアウトを使用するには、またはカスタマイズするには? とは異なります。.

これが私がやろうとしていることの実例です: 私がやろうとしていることの図

UICollectionViewFlowLayout、そのサブクラスでこれを行うことができるかどうか、または完全にカスタムのレイアウトを作成する必要があるかどうか疑問に思っていますか? UICollectionView の WWDC 2012 ビデオに基づいて、フロー レイアウトを垂直スクロールで使用するとレイアウト ラインが水平になり、水平スクロールするとレイアウト ラインが垂直になるようです。水平スクロール コレクション ビューに水平レイアウト ラインが必要です。

また、モデルに固有のセクションはありません。これは単一のアイテム セットです。それらをセクションにグループ化することもできますが、コレクション ビューはサイズ変更可能であるため、ページに収まるアイテムの数が時々変化し、各アイテムがどのページに表示されるかを選択するよりも、レイアウトに任せたほうがよいようです。意味のあるセクションがない場合はモデル。

フロー レイアウトでこれを行うことはできますか、それともカスタム レイアウトを作成する必要がありますか?

4

8 に答える 8

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

.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

最後に、ページ分割された動作が必要な場合は、UICollectionView を構成するだけです。

_collectionView.pagingEnabled = YES;

十分に役立つことを願っています。

于 2014-01-17T13:00:09.260 に答える
12

おっしゃるとおり、横スクロール コレクション ビューがセルをレイアウトする方法とは異なります。残念ながら、独自のカスタムUICollectionViewLayoutサブクラスを実装する必要があります。それか、モデルをセクションに分けます。

于 2013-05-21T20:09:30.487 に答える
0

UICollectionView.xib の Scroll Direction を Horizo​​ntal に変更するだけです。そして、配列内の要素の正しい順序で使用してください。

于 2015-03-16T16:03:02.790 に答える
-1
@interface HorizontalCollectionViewLayout : UICollectionViewFlowLayout

@end

@implementation HorizontalCollectionViewLayout

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
        self.minimumLineSpacing = 0;
        self.minimumInteritemSpacing = 0;
    }
    return self;
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSArray *attributesArray = [super layoutAttributesForElementsInRect:rect];

    NSInteger verticalItemsCount = (NSInteger)floorf(self.collectionView.bounds.size.height / self.itemSize.height);
    NSInteger horizontalItemsCount = (NSInteger)floorf(self.collectionView.bounds.size.width / self.itemSize.width);
    NSInteger itemsPerPage = verticalItemsCount * horizontalItemsCount;

    for (NSInteger i = 0; i < attributesArray.count; i++) {
        UICollectionViewLayoutAttributes *attributes = attributesArray[i];
        NSInteger currentPage = (NSInteger)floor((double)i / (double)itemsPerPage);
        NSInteger currentRow = (NSInteger)floor((double)(i - currentPage * itemsPerPage) / (double)horizontalItemsCount);
        NSInteger currentColumn = i % horizontalItemsCount;
        CGRect frame = attributes.frame;
        frame.origin.x = self.itemSize.width * currentColumn + currentPage * self.collectionView.bounds.size.width;
        frame.origin.y = self.itemSize.height * currentRow;
        attributes.frame = frame;
    }
    return attributesArray;
}

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
    return YES;
}

@end
于 2015-07-28T05:31:25.803 に答える