0

Currently I have a standard UICollectionViewFlowLayout scrolling horizontally, but it stack items onto each other, if the height allows.
Instead I want to have a collection view that scrolls horizontally and does no line breaking, i.e. does not stack items onto each other - simply a linear flow.
This problem is even simpler than in this question, because vertical scrolling is not required. Therefore the answer there does not apply.

I think I could subclass UICollectionViewFlowLayout, but I am not sure which methods had to be overwritten.
I checked that currently -layoutAttributesForElementsInRect: is called before the items are displayed (but not -layoutAttributesForItemAtIndexPath:).
The NSArray returned by -layoutAttributesForElementsInRect: contains for every item a frame (some of them are stacked onto each other).
I can suppress this stacking in -collectionView:layout:sizeForItemAtIndexPath:, if I increase in this method the height of all items to be greater than half the maximum height, but then the items itself are scaled vertically, which I don't want.

So my question is: What and how do I overwrite methods of UICollectionViewFlowLayout or UICollectionViewLayout to get this linear flow?

Community
  • 1
  • 1
Reinhard Männer
  • 14,022
  • 5
  • 54
  • 116

1 Answers1

0

I solved the problem by myself, but maybe this helps somebody else.
I used partly the UICollectionView custom layout tutorial of Bryan Hansen:

I subclassed UICollectionViewFlowLayout, added properties, and implemented only 3 methods (pseudo code below):

@interface LinearCollectionViewFlowLayout : UICollectionViewFlowLayout
@end  

@interface LinearCollectionViewFlowLayout ()
@property (nonatomic, strong) NSArray *layoutInfo;    // layout infos for the collection view elements
@property                     CFFloat *maxItemHeight; // max height of items in the collection view
@end

@implementation LinearCollectionViewFlowLayout

- (void)prepareLayout
{
    // Get from the collection views datasource an array of all items in the collection view
    ......

    // Loop through all items and store max height in the property.
    ......

    CGFloat xOffsetOfNextItem = 0;
    NSMutableArray *tmpLayoutInfo = [NSMutableArray array];
    // Loop through all items to compute x and y origin of every item in the collection view
    for (int index = 0; index < nrCollectionViewItems; index++) {
        // Get the next item via its index and store it in a variable nextItem
        ......
        CGSize  nextItemSize   = nextItem.size;       // Get the size of the next item
        CGFloat nextItemHeight = nextItemSize.height; // Get the height of the next item

        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:0]; // Assuming that there is only 1 section in the collection view
        UICollectionViewLayoutAttributes *itemAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
        CGFloat yOffset = self.maxItemHeight/2.0 - nextImageHeight/2.0 + 5; // center the item vertically (add spacer)
        CGRect itemFrame = CGRectMake(xOffsetOfNextItem, yOffset, nextItemSize.width, nextItemHeight);
        itemAttributes.frame = itemFrame; // store the frame of the item
        tmpLayoutInfo[index] = itemAttributes;

        xOffsetOfNextIcon += nextItemSize.width + 5; // Compute x of next item (add a spacer)
    }
    self.collectionView.contentWidth = xOffsetOfNextItem; // Store the width of the collection view

    self.layoutInfo = [NSArray arrayWithArray:tmpLayoutInfo]; // Store all layout infos
}

//-----------------------------------------------------------------------------------------------------------------

- (CGSize)collectionViewContentSize
{
    CGSize size = [super collectionViewContentSize]; // Get the collection view size as computed by UICollectionViewFlowLayout
    size.width = self.collectionView.contentWidth;   // Replace the width by the value computed in prepareLayout
    return size;
}

//-----------------------------------------------------------------------------------------------------------------

-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect
{
    // Set up an array of layout attributes only for the items in rect
    NSMutableArray *attributesForElementsInRect = [NSMutableArray array];
    for (UICollectionViewLayoutAttributes *nextAttributes in self.layoutInfo) {
        CGRect nextFrame = nextAttributes.frame;
        if (CGRectIntersectsRect(rect, nextFrame)) {
            [attributesForElementsInRect addObject:nextAttributes];
        }
    }

    return [NSArray arrayWithArray:attributesForElementsInRect];
}

@end
Reinhard Männer
  • 14,022
  • 5
  • 54
  • 116