2
\$\begingroup\$

I have a "cumulative" lookup array for a custom collection view layout.

The idea is that I have an array of rectangles, each with an index. The index is not unique, and is monotonically increasing. I need to create a lookup table, such that given a cell index, it returns the accumulated height of all rects lower than that index.

Here is my implementation. I am wondering if it can be improved (for maintainability) using functional programming, or an established algorithm. Nested loops always raises a flag for me:

var offsets: [CGFloat] = [0]

while metas.count > 0 {
    // meta is a struct that holds an index and rect.
    let meta = metas.removeAtIndex(0)

    while offsets.count <= meta.index {
        // copy the previous value, to maintain accumulation
        offsets.append(offsets[offsets.count - 1])
    }

    offsets[meta.index] += meta.rect.height
}

let cellCount = self.collectionView?.numberOfItemsInSection(0) ?? 0

// need to pad the array at the end
while offsets.count < cellCount {
    offsets.append(offsets[offsets.count - 1])
}

Background:

I am implementing my own collection view layout. It has sticky headers, custom supplementary views, and other customizations. To implement a custom layout, 4 critical methods must be implemented:

override func collectionViewContentSize() -> CGSize {
    let width = self.collectionView?.bounds.width ?? 0
    let height = allLayoutAttributes.last?.frame.maxY ?? 0

    return CGSize(width: width, height: height)
}

override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? {
    return allLayoutAttributes.filter {
        $0.frame.intersects(rect)
    }
}

override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes! {
    return cellLayoutAttributes[indexPath.item]
}

override func layoutAttributesForSupplementaryViewOfKind(elementKind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes! {
    return supplementaryLayoutAttributes[indexPath.item]
}

As you can see from the implementation, I am preferring computing everything ahead of time, and then just returning the attributes when the methods are called. Now, since supplementary views are inlined with the cells, here is how I was computing things:

  1. Since cell height is fixed, calculate the position of all supplementary view. I know all their offsets, but they vary in height.
  2. cells need to be offset by all the cell heights before them + supplementary heights.

The approach I took above was to compute supplementary view in isolation, get it over with, create a lookup table for the offsets they represent, then iterate the cells and apply the offset as required.

\$\endgroup\$
5
  • \$\begingroup\$ I ended up merging the cell and supplementary view code, by subclassing UICollectionViewLayout directly. \$\endgroup\$
    – Mazyod
    Nov 8, 2014 at 10:55
  • \$\begingroup\$ Why do you need the accumulated height? \$\endgroup\$
    – nhgrif
    Nov 8, 2014 at 13:21
  • \$\begingroup\$ @nhgrif for an easy look up table. given a cell index (0, 1, 2, ...) the array will contain the accumulation of all previous heights, thus marking the appropriate offset. \$\endgroup\$
    – Mazyod
    Nov 8, 2014 at 13:30
  • \$\begingroup\$ Table views and collection views have a method for scrolling to a cell at a given index path. \$\endgroup\$
    – nhgrif
    Nov 8, 2014 at 13:32
  • 1
    \$\begingroup\$ @Rob, absolutely. I'll write a thorough explanation. \$\endgroup\$
    – Mazyod
    Nov 9, 2014 at 6:36

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.