A post mortem on my many flow layout mistakes and a look at the cool things you can do when you write your own collection view layout. Was followed by demos and review of layout code from https://github.com/bnickel/SEUICollectionViewLayout
layout views: • Set it and forget it (setFrame:/setBounds:/setCenter:) • layoutSubviews ([super layoutSubviews]) • autoresizingMask (springs and struts) • AutoLayout (A = B*C +D with priority E) • It also provides two big layout managers: • UITableView (How tall are you?) • UICollectionViewFlowLayout (What size are you? Down or left?)
or thousands of objects! • They only create enough views for what can fit on the screen. • Cell reuse. The best of garbage collection at your fingertips. • Self-contained cell layouts. Less interdependencies than AutoLayout.
effort making your models match with its. if indexPath.section != 0 { if indexPath.row != 0 && indexPath.row != items[indexPath.section].count - 1 { // Something just for inner elements of additional sections. } } • You expend huge amounts of effort working around their limitations, often with weird tricks. • Left aligned cells (https://github.com/nilsou/ NHAlignmentFlowLayout) • Single row of cells (http://stackoverflow.com/q/13918276/860000) • Decorations (http://stackoverflow.com/q/17592640/860000)
scrolling collection view with a variety of possible cell types and separators on all but the last cell. • Disclaimer: Some of the approaches we used overlap each other and could be simplified. This was done piecemeal by multiple developers over a span of time.
sizeForCellAtIndexPath: and sometimes two small cells would be placed vertically if next to a big one. • Implemented solution: Subclass UICollectionViewFlowLayout and override layoutAttributesForItemAtIndexPath: to move items with non-zero y-values over and up. • Better solution: Set itemSize = (collectionViewHeight, itemWidth)
lines on all but the last cell. • Implemented solution: Create a single host cell with a with a line to be hidden on the last item. Then implement our own reuse system based on Nibs and renderer ID’s. This had many nasty iterations where we did things like let the last cell contents use the extra width and had many different reuse identifiers for both the collection view cells and the table view cells. • Better Solution: Subclass UICollectionViewFlowLayout and implement a decoration view.
we could have done but we were new to this and collection views do not lend themselves to obvious configuration. • With a limited and soured impression of collection views, I set off to implement a much more complex UI…
Each section has a header, a decorative circle and a line running its whole length. • Each group of cells has its own header. • If two adjacent groups of cells are small enough to share a row, the do. Big groups wrap and don’t share. • All items on the same row have the same height. • Banner ads get their own line. • No border on the rightmost cell. • When you rotate the screen you want to be in roughly the same spot.
of view reuse code could be dropped allowing the exact same cells to be reused by the two very different layouts. @interface SESingleFeedGroupCollectionViewLayout : SEUICollectionViewLayout @property (nonatomic, assign) CGFloat itemWidth; @property (nonatomic, assign) CGFloat itemSpacing; @property (nonatomic, readonly) CGFloat pageWidth; @end
of view reuse code could be dropped allowing the exact same cells to be reused by the two very different layouts. @interface SESingleFeedGroupCollectionViewLayout : SEUICollectionViewLayout @property (nonatomic, assign) CGFloat itemWidth; @property (nonatomic, assign) CGFloat itemSpacing; @property (nonatomic, readonly) CGFloat pageWidth; @end CELLS CELLS CELLS CELLS CELLS CELLS CELLS CELLS CELLS CELLS
of view reuse code could be dropped allowing the exact same cells to be reused by the two very different layouts. @interface SESingleFeedGroupCollectionViewLayout : SEUICollectionViewLayout @property (nonatomic, assign) CGFloat itemWidth; @property (nonatomic, assign) CGFloat itemSpacing; @property (nonatomic, readonly) CGFloat pageWidth; @end CELLS CELLS CELLS CELLS CELLS CELLS CELLS CELLS CELLS CELLS
2. UICollectionView calls prepareLayout. 3. prepareLayout (your code) lays out UICollectionViewLayoutAttributes (placeholders for view) with setFrame:/setBounds:/setCenter: (Seem familiar?). It also determines contentSize. This can be ugly. 4. UICollectionView manages animation, though it could use some help. 5. When determining what cells to show when scrolling, UICollectionView calls layoutAttributesForElementsInRect: 6. When resizing (adaptive layout or orientation changes), you have a chance to recenter your view with targetContentOffsetForProposedContentOffset:
• Sections are inserted/removed separately from cells. I just avoid that where possible. • In batch mode, you insert/move cells to their final destination. There’s no simple mapping API to get “Cell move from Index Path X to Y.” You can derive it. I do something easier. • Supplementary view movement animation is a little rough. I fake things by deleting and reinserting all sup views and managing the animation. • Animating both a size change and a scroll position change is broken. I just do one or the other. • Animating from offscreen to on is also flakey. Can be fixed by growing your view. https://github.com/jpsim/UICollectionView-Animation-Bug
lot of the challenges related to collection view layouts to take the pain out of animating them. • We can look at SEWideFeedCollectionViewLayout. (Objective-C, closed source) or the simpler SurveyCollectionViewLayout example project (Swift, open source). The latter does really cool stuff in the model layer.