8

What is the best method to provide a footer for a UICollectionView that "sticks" to the bottom of the screen bounds on a given page? Assume the UICollectionView is full screen and only has one section.

Currently I am providing a UICollectionReusableView object in collectionView:viewForSupplementaryElementOfKind:atIndexPath:, but (as one might expect) when the content of my collection exceeds this screen's bounds, the footer is pushed off-screen.

I'm guessing the key is a Decoration View - but I can't find any good code (non-IB) examples on how these work, and Apple's documentation is in my opinion unclear on this particular subject.

Update re: Decoration Views

After building out and experimenting with a Decoration View (using this tutorial), I hit some limitations - namely that there aren't really any callbacks between the Decoration View object and your UICollectionViewController controller object (the Decoration View is managed by a UICollectionViewLayout object, not the UICollectionViewController object). It seems Apple was very serious about Decoration Views being limited to visual adornments, and not data-driven (although you could obviously hack around this).

So, the "right" solution still eludes me, but in the mean time I just created a static UIView object and am just managing that from my UICollectionViewController object. It works OK, but feels wrong.

Update re: Sticky HEADERS

Over the last few months, I've worked on similar issues across various projects, and did recently find a solution for sticky HEADERS. I assume the same would apply to footers, but I haven't tested it.

Details about headers here:

How to make Supplementary View float in UICollectionView as Section Headers do in UITableView plain style

The implementation is pretty heavy, but it seems to work well in most circumstances.

If there is no further activity on this question soon, I will close as a duplicate and point to the article above.

Community
  • 1
  • 1
toblerpwn
  • 5,415
  • 8
  • 38
  • 46

3 Answers3

17

Basically what you need to do is provide a custom UICollectionViewLayout subclass that invalidates itself when the bounds change (when the view scrolls the bounds change). And then provides UICollectionViewLayoutAttributes for the supplementary view where the center is updated to hug the current bounds of the collection view (top, bottom, left, right, whatever).

I added project on github that demonstrates this strategy.

UPDATE: as of iOS 9, UICollectionViewFlowLayout has two very handy properties that simplify this task drastically. See sectionHeadersPinToVisibleBounds and sectionFootersPinToVisibleBounds.

Derrick Hathaway
  • 1,087
  • 11
  • 14
  • 1
    @pxlshpr it shouldn't be, this is super inefficient – TheCodingArt Jul 25 '16 at 21:34
  • 2
    Inefficient or not, invalidating the layout when bounds change is exactly how Apple appears to have implemented the iOS 9 pinning. `invalidateLayout` is called several times per second when scrolling with pinning turned on. – jscs Aug 28 '16 at 04:45
0

ok.. so I have tried updating code from below link. and it works. https://teamtreehouse.com/community/add-sticky-footer-to-uicollectionview-in-swift

class StickyFooter : UICollectionViewFlowLayout {

var footerIsFound             : Bool = false
var UICollectionAttributes    : [UICollectionViewLayoutAttributes]?


override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
    return true
}


override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
{
    UICollectionAttributes = super.layoutAttributesForElements(in: rect)

    for  attributes in UICollectionAttributes! {

        if let type = attributes.representedElementKind {

            if type == UICollectionElementKindSectionFooter
            {
                footerIsFound = true
                updateFooter(attributes: attributes)
            }
        }
    }

    if (!self.footerIsFound) {

        let newItem = self.layoutAttributesForSupplementaryView(ofKind: UICollectionElementKindSectionFooter, at : NSIndexPath(row: self.UICollectionAttributes!.count, section: 0) as IndexPath)


        UICollectionAttributes?.append(newItem!)

    }

    return UICollectionAttributes
}

override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes?
{
    let attributes = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: elementKind, with: indexPath)

    attributes.size = CGSize(width: self.collectionView!.bounds.size.width, height: 75)

    if elementKind.isEqualToString(find: UICollectionElementKindSectionFooter)
    {
        updateFooter(attributes: attributes)
    }
    return attributes
}


func updateFooter(attributes : UICollectionViewLayoutAttributes){
    let currentBounds = self.collectionView?.bounds
    attributes.zIndex = 1024
    attributes.isHidden = false
    let yOffset = currentBounds!.origin.y + currentBounds!.size.height - attributes.size.height/2.0
    attributes.center = CGPoint(x: currentBounds!.midX, y: yOffset)

}}
pankti patel
  • 31
  • 1
  • 8
-6

UICollectionViewController (like UITableVC) is a "shortcut", these 2 classes are just overriding UIViewController, create the collectionView for you.

You can easily do that and add your sticky view on your own.

Thomas Decaux
  • 21,738
  • 2
  • 113
  • 124
  • I have learned more about this over the course of various projects in the last few months (since posting this question), and IMHO this is far, far from easy. I will update my original question for future searchers, but if you know of an easy way to accomplish the above, please elaborate. – toblerpwn Apr 17 '13 at 18:30