How to get number of items from collectionview's data source - ios

I am new to Collectionview, but not to iOS. I am using custom Flowlayout for my collectionview. I need to return contentsize based on current number of items returned by CollectionView's data source. Is there anyway to know how many items are there currently in flowlayout's collectionview?
#interface LatestNewsFlowLayout : UICollectionViewFlowLayout
-(id)initWithSize:(CGSize) size;
#end
#implementation LatestNewsFlowLayout
-(id)initWithSize:(CGSize) size {
self = [super init];
if (self) {
self.itemSize = size;
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
self.sectionInset = UIEdgeInsetsMake(0, 10.0, 0, 0);
self.minimumLineSpacing = 5;
}
return self;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)oldBounds {
return YES;
}
-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect {
NSArray *answer = [[super layoutAttributesForElementsInRect:rect] mutableCopy];
for(int i = 1; i < [answer count]; ++i) {
UICollectionViewLayoutAttributes *currentLayoutAttributes = answer[i];
UICollectionViewLayoutAttributes *prevLayoutAttributes = answer[i - 1];
NSInteger maximumSpacing = 5;
NSInteger origin = CGRectGetMaxX(prevLayoutAttributes.frame);
if(origin + maximumSpacing + currentLayoutAttributes.frame.size.width < self.collectionViewContentSize.width) {
CGRect frame = currentLayoutAttributes.frame;
frame.origin.x = origin + maximumSpacing;
currentLayoutAttributes.frame = frame;
}
}
return answer;
}
-(CGSize) collectionViewContentSize
{
CGSize size;
int numOfItems = ???
size.width = 100 * numOfItems;
size.height = 100;
return size;
}

The UICollectionViewFlowLayout is a subclass of UICollectionViewLayout. It should therefore have access to the UICollectionView which should know how many items there are.
[self.collectionView numberOfItemsInSection:0];
You may need to iterate over the section to get the total number of items if you have more that one section.
You can get the number of sections similarly:
[self.collectionView numberOfSections];
Hope this helps! :)

Swifty solution is next:
let itemsCount = Array(0..<collectionView.numberOfSections)
.map { collectionView.numberOfItems(inSection: $0) }
.reduce(0, +)
Now itemsCount contains total number of collection view items across all sections.

If you make a property/outlet called collectionView, try this:
for (int i = 0; i<[collectionView numberOfSections];i++) {//Iterate through all the sections in collectionView
for (int j = 0; j<[collectionView numberOfItemsInSection:i]; j++) {//Iterate through all the rows in each section
numOfItems++;
}
}

In Swift, you can write the following (provided that you have a data source with sections and items):
let totalItemCount = sections.reduce(0) { result, section -> Int in
return result + section.items.count
}
Just replace section with your data source, and items with the item collection name.

+ (NSInteger)countInCollectionView:(UICollectionView *)collectionView {
NSInteger itemCount = 0;
for( int section = 0; section < [collectionView numberOfSections]; ++section ){
itemCount += [collectionView numberOfItemsInSection:section];
}
return itemCount;
}

I like Vadim's answer because it is swifty and clean, but we're creating an array from a range and looping it twice (map and reduce). I'd suggest a similar solution using directly a range with only one loop (reduce):
let totalItems = (0..<collectionView.numberOfSections)
.reduce(0) { res, cur in
res + collectionView.numberOfItems(inSection: cur)
}
In my case I ended up creating an extension for UICollectionView:
extension UICollectionView {
var totalItems: Int {
(0..<numberOfSections).reduce(0) { res, cur in
res + numberOfItems(inSection: cur)
}
}
}
And then just:
collectionView.totalItems

Related

UICollectionView throws uncaught exception using custom layout

I'm using collection view with custom layout, Calling webservice I received first time from the API returns 20 objects, and the second time it will return 1 object, while reloading the data applications throws the following error.
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: UICollectionView received layout attributes for a cell with an index
path that does not exist: {length = 2, path
= 0 - 0}
Code piece of creating new layout
-(void)doNewLayout
{
id<UICollectionViewDelegateJSPintLayout> delegate = (id<UICollectionViewDelegateJSPintLayout>)self.collectionView.delegate;
// get column width from delegate. If the method isn't implemented fall back to our property
NSUInteger columnWidth = self.columnWidth;
if(delegate && [delegate respondsToSelector:#selector(columnWidthForCollectionView:layout:)])
{
columnWidth = [delegate columnWidthForCollectionView:self.collectionView
layout:self];
}
// find out how many cells there are
NSUInteger cellCount = [self.collectionView numberOfItemsInSection:0];
// get max number of columns from the delegate. If the method isn't implemented, fall back to our property
NSUInteger maximumNumberOfColumns = self.numberOfColumns;
if(delegate && [delegate respondsToSelector:#selector(maximumNumberOfColumnsForCollectionView:layout:)]){
maximumNumberOfColumns = [delegate maximumNumberOfColumnsForCollectionView:self.collectionView layout:self];
}
// build an array of all the cell heights.
NSMutableArray* cellHeights = [NSMutableArray arrayWithCapacity:cellCount];
for(NSUInteger cellIndex = 0; cellIndex < cellCount; ++cellIndex)
{
CGFloat itemHeight = self.itemHeight; // set default item size, then optionally override it
if(delegate && [delegate respondsToSelector:#selector(collectionView:layout:heightForItemAtIndexPath:)])
{
itemHeight = [delegate collectionView:self.collectionView
layout:self
heightForItemAtIndexPath:[NSIndexPath indexPathForItem:cellIndex
inSection:0]];
}
cellHeights[cellIndex] = #(itemHeight);
}
// now build the array of layout attributes
self.pendingLayoutAttributes = [NSMutableArray arrayWithCapacity:cellCount];
// will need an array of column heights
CGFloat* columnHeights = calloc(maximumNumberOfColumns,sizeof(CGFloat)); // calloc() initializes to zero.
CGFloat contentHeight = 0.0;
CGFloat contentWidth = 0.0;
for(NSUInteger cellIndex = 0; cellIndex < cellCount; ++cellIndex)
{
CGFloat itemHeight = [cellHeights[cellIndex] floatValue];
// find shortest column
NSUInteger useColumn = 0;
CGFloat shortestHeight = DBL_MAX;
for(NSUInteger col = 0; col < maximumNumberOfColumns; ++col)
{
if(columnHeights[col] < shortestHeight)
{
useColumn = col;
shortestHeight = columnHeights[col];
}
}
NSIndexPath* indexPath = [NSIndexPath indexPathForItem:cellIndex
inSection:0];
UICollectionViewLayoutAttributes* layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
layoutAttributes.size = CGSizeMake(columnWidth,itemHeight);
layoutAttributes.center = CGPointMake((useColumn * (columnWidth + self.interitemSpacing)) + (columnWidth / 2.0),columnHeights[useColumn] + (itemHeight / 2.0));
self.pendingLayoutAttributes[cellIndex] = layoutAttributes;
columnHeights[useColumn] += itemHeight;
if(columnHeights[useColumn] > contentHeight)
contentHeight = columnHeights[useColumn];
CGFloat rightEdge = (useColumn * (columnWidth + self.interitemSpacing)) + columnWidth;
if(rightEdge > contentWidth)
contentWidth = rightEdge;
columnHeights[useColumn] += self.lineSpacing;
}
self.contentSize = CGSizeMake(contentWidth,contentHeight+100);
free(columnHeights);
}
Any Quick solution would be really appreciated.Thanks
There are couple of things need consideration , as you are custom layout then you need to create layoutAttribute for each indexPath. In your case your data source array count and offerModel.arrayOffers and self.pendingLayoutAttributes should be same, if not then it might be problem and its crushable problem if offerModel.arrayOffershave more items then self.pendingLayoutAttributes.
If you are loading data async then make sure when you are adding rows in arraysOffers also add layoutAttributes in customLayout pendLayoutAttributes,which I think you are not doing at the moment, do that by adding a method and provide new indexPaths to that which create layoutAttributes.
I usually do like this
- (void)insertItems:(NSArray*)items
{
NSInteger startIndex = <start index for new item>;
[self.items addObjectsFromArray:items];
[self calculateLayoutForItems:items startIndex:startIndex];
}
This method will calculate layoutAttributes
- (void)calculateLayoutForItems:(NSArray*)items startIndex:(NSInteger)startIndex
{
// collection view and loop over them as well and nest indexPath creation
NSInteger section = 0;
NSInteger endIndex = self.items.count;
// Lets create indexPaths for provided items and get there frames
for (NSInteger index = startIndex ;index < endIndex; index++)
{
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:section];
CGRect frame = [self frameForItemAtIndexPath:indexPath];// call your method for frame at indexPath
UICollectionViewLayoutAttributes *itemAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
itemAttributes.frame = frame;
self.pendingLayoutAttributes[indexPath] = itemAttributes; // you can use your index
}
}
Now when you got more items in data source call this method on customLayoutObject [self.customLayout insertItems:newItemsArray];
Also if you have stored properties then its worth overriding invalidate method in custom layout to reset all properties to initial state,Then
you can just invalidate customLayout before reloadData method, then collection view will force it to compute layout again.

UICollectionView: How to keep the items of a section in a single row?

I would like to have three sections inside a UICollectionView.
Each section shall display its items in the same row. If the items won't fit on the screen they should still remain in the same row and the user should scroll to right to see them all.
In a nutshell, I would like to prevent the items flow into next line. Hope it makes sense.
I have found some strange solutions as here. But these don't really work, if you have more than one section.
How can I tackle this problem? Is there a setting on Storyboard or do I have to use a custom Layout afterall?
Use multiple UICollectionViews, each with its own scrollable area.
I wrote this layout a while ago, but I think it should still work. The data source is an array of arrays, so each row is a new section. Here's the flow layout class,
#define space 5
#import "MultpleLineLayout.h"
#implementation MultpleLineLayout { // a subclass of UICollectionViewFlowLayout
NSInteger itemWidth;
NSInteger itemHeight;
}
-(id)init {
if (self = [super init]) {
itemWidth = 60;
itemHeight = 60;
}
return self;
}
-(CGSize)collectionViewContentSize {
NSInteger xSize = [self.collectionView numberOfItemsInSection:0] * (itemWidth + space); // "space" is for spacing between cells.
NSInteger ySize = [self.collectionView numberOfSections] * (itemHeight + space);
return CGSizeMake(xSize, ySize);
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path {
UICollectionViewLayoutAttributes* attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:path];
attributes.size = CGSizeMake(itemWidth,itemHeight);
long xValue = itemWidth/2 + path.row * (itemWidth + space);
long yValue = itemHeight + path.section * (itemHeight + space);
attributes.center = CGPointMake(xValue, yValue);
return attributes;
}
-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect {
NSInteger minRow = (rect.origin.x > 0)? rect.origin.x/(itemWidth + space) : 0; // need to check because bounce gives negative values for x.
NSInteger maxRow = rect.size.width/(itemWidth + space) + minRow;
NSMutableArray* attributes = [NSMutableArray array];
for(NSInteger i=0 ; i < self.collectionView.numberOfSections; i++) {
for (NSInteger j=minRow ; j < maxRow; j++) {
NSIndexPath* indexPath = [NSIndexPath indexPathForItem:j inSection:i];
[attributes addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
}
}
return attributes;
}

iOS - Creating a SpringBoard screen

I'm trying so create a collection view that behaves like the iOS springboard screen.
That means that I want to have a collection view, with all of my items, and to have a page controller underneath the grid (just like the springboard screen). I also want to implement the same edit mode, meaning that one my icons are in edit mode, I can drag them from one page to another, in such a way that a page doesn't have to be full.
I've tried to set a single collection view to scroll horizontally, but it doesn't act as I want. first of all, the icons are added one below the other, instead of one beside the other. secondly, I don't have the page dots this way, and the user can't put icons on different pages
I also tried to set a page controller, and put in every page the collection view. In this case, I get that while in edit mode, I can't go between screens. I guess that is because I have more that one collection view, and they don't interact with each other.
I though of another way, of using a scroll view, and try to put a collection view inside that scroll view. But it sounds to me like a very not elegant solution.
Does anyone have an idea about how to create such a scene?
Edit:
I think the answer for my problem is hiding in this question. It describes the same situation I want to have: a collection view, that is scrolled horizontally, with paging, and that the order of the icons is not flowing vertically, but also flowing horizontally. There is a partially good answer there. I say it's partially good since the answer describes a subclass of UICollectionViewFlowLayout, but in the sample project uploaded the subclass is of UICollectionViewLayout, and I think the whole point was to maintain the functions that the UICollectionViewFlowLayout has.
This question was asked a year ago, so I believe someone has an answer, or at least, I hope.
I have partial solution to my question.
I did use the answer (of Bogdan) to the linked question above. Here's the code:
layout.h:
#import <UIKit/UIKit.h>
#interface V9Layout : UICollectionViewLayout
#property (nonatomic, assign) NSInteger itemsInOneRow;
#property (nonatomic, assign) NSInteger itemsInOneCol;
#property (nonatomic, assign) CGSize itemSize;
#end
layout.m:
#import "V9Layout.h"
#interface V9Layout()
-(void)calculateLayoutProperties;
-(int)pagesInSection:(NSInteger)section;
#property (nonatomic, strong) NSMutableArray *frames;
#property (nonatomic, assign) CGFloat lineSpacing;
#property (nonatomic, assign) CGFloat interitemSpacing;
#property (nonatomic, assign) CGSize pageSize;
#end
#implementation V9Layout
#synthesize itemsInOneRow = _itemsInOneRow, itemsInOneCol = _itemsInOneCol, itemSize = _itemSize;
#synthesize lineSpacing = _lineSpacing, interitemSpacing = _interitemSpacing;
#synthesize frames = _frames;
- (id)init
{
self = [super init];
if (self) {
[self setup];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self) {
[self setup];
}
return self;
}
- (void)setup
{
self.itemsInOneRow = 3;
self.itemsInOneCol = 4;
self.frames = [NSMutableArray array];
}
-(void)calculateLayoutProperties {
self.pageSize = self.collectionView.bounds.size;
// The width of all line spacings is equal to width of ONE item. The same for interitem spacing
CGFloat itemWidth = self.pageSize.width / (self.itemsInOneRow + 1); // +1 because we all spaces make width of one additional item
CGFloat itemHeight = self.pageSize.height / (self.itemsInOneCol + 1); // the same
NSLog(#"calculateLayoutProperties item width: %f, item height: %f", itemWidth, itemHeight);
self.itemSize = CGSizeMake(itemWidth, itemHeight);
// this part is only to make a global parameter, because I'm using this data to create the size of the UICollectionViewCell. I think I need to use a delegate method for the UICollectionViewCell to ask the UICollectionViewLayout for the calculated size of cell.
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
if (standardUserDefaults) {
[standardUserDefaults setObject:[NSNumber numberWithFloat:itemWidth] forKey:#"cellWidth"];
[standardUserDefaults setObject:[NSNumber numberWithFloat:itemHeight] forKey:#"cellHeight"];
[standardUserDefaults synchronize];
}
// spacing for x
self.lineSpacing = (self.pageSize.width - (self.itemsInOneRow * self.itemSize.width)) / (self.itemsInOneRow + 1);
// spacing for y
self.interitemSpacing = (self.pageSize.height - (self.itemsInOneCol * self.itemSize.height)) / (self.itemsInOneCol + 1);
}
-(int)pagesInSection:(NSInteger)section {
NSLog(#"v9layout: numberOfItemsInSection is %ld", (long)[self.collectionView numberOfItemsInSection:section]);
return ([self.collectionView numberOfItemsInSection:section] - 1) / (self.itemsInOneRow * self.itemsInOneCol) + 1;
}
-(CGSize)collectionViewContentSize {
// contentSize.width is equal to pages * pageSize.width
NSInteger sections = 1;
if ([self.collectionView respondsToSelector:#selector(numberOfSections)]) {
sections = [self.collectionView numberOfSections];
}
int pages = 0;
for (int section = 0; section < sections; section++) {
pages = pages + [self pagesInSection:section];
}
return CGSizeMake(pages * self.pageSize.width, self.pageSize.height);
}
-(void)prepareLayout {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self calculateLayoutProperties];
});
[self.frames removeAllObjects];
NSInteger sections = 1;
if ([self.collectionView respondsToSelector:#selector(numberOfSections)]) {
sections = [self.collectionView numberOfSections];
}
int pagesOffset = 0; // Pages that are used by prevoius sections
int itemsInPage = self.itemsInOneRow * self.itemsInOneCol;
for (int section = 0; section < sections; section++) {
NSMutableArray *framesInSection = [NSMutableArray array];
int pagesInSection = [self pagesInSection:section];
int itemsInSection = [self.collectionView numberOfItemsInSection:section];
for (int page = 0; page < pagesInSection; page++) {
int itemsToAddToArray = itemsInSection - framesInSection.count;
int itemsInCurrentPage = itemsInPage;
if (itemsToAddToArray < itemsInPage) { // If there are less cells than expected (typically last page of section), we go only through existing cells.
itemsInCurrentPage = itemsToAddToArray;
}
for (int itemInPage = 0; itemInPage < itemsInCurrentPage; itemInPage++) {
CGFloat originX = (pagesOffset + page) * self.pageSize.width + self.lineSpacing + (itemInPage % self.itemsInOneRow) * (self.itemSize.width + self.lineSpacing);
CGFloat originY = self.interitemSpacing + (itemInPage / self.itemsInOneRow) * (self.itemSize.height + self.interitemSpacing);
CGRect itemFrame = CGRectMake(originX, originY, self.itemSize.width, self.itemSize.height);
[framesInSection addObject:NSStringFromCGRect(itemFrame)];
}
}
[self.frames addObject:framesInSection];
pagesOffset += pagesInSection;
}
}
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
NSMutableArray *attributes = [NSMutableArray array];
NSInteger sections = 1;
if ([self.collectionView respondsToSelector:#selector(numberOfSections)]) {
sections = [self.collectionView numberOfSections];
}
int pagesOffset = 0;
int itemsInPage = self.itemsInOneRow * self.itemsInOneCol;
for (int section = 0; section < sections; section++) {
int pagesInSection = [self pagesInSection:section];
int itemsInSection = [self.collectionView numberOfItemsInSection:section];
for (int page = 0; page < pagesInSection; page++) {
CGRect pageFrame = CGRectMake((pagesOffset + page) * self.pageSize.width, 0, self.pageSize.width, self.pageSize.height);
if (CGRectIntersectsRect(rect, pageFrame)) {
int startItemIndex = page * itemsInPage;
int itemsInCurrentPage = itemsInPage;
if (itemsInSection - startItemIndex < itemsInPage) {
itemsInCurrentPage = itemsInSection - startItemIndex;
}
for (int itemInPage = 0; itemInPage < itemsInCurrentPage; itemInPage++) {
UICollectionViewLayoutAttributes *itemAttributes = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:startItemIndex + itemInPage inSection:section]];
if (CGRectIntersectsRect(itemAttributes.frame, rect)) {
[attributes addObject:itemAttributes];
}
}
}
}
pagesOffset += pagesInSection;
}
return attributes;
}
-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
attributes.frame = CGRectFromString(self.frames[indexPath.section][indexPath.item]);
return attributes;
}
#end
This implement the horizontal paging with order of cells one beside each other and not one underneath each other.
I still want to add a drag and drop ability so that the user can change the order of cells. I poset this question about it.
Hopefully, I'll find answer to this and update this answer.

UICollectionViewFlowLayout implementation

I am trying to accomplish the following, I've done this easily with UIScrollview but I've been experimenting with UICollectionView lately (I know I'm pretty late to the game) and would love to know if in order to do what I want I have to implement a custom layout or if FlowLayout already does this for me.
Basically, if you look at the attachment, you will notice that scrolling can happen both vertically and horizontally, the rows go all the way to beyond the UICollectionView height. The same happens with columns going beyond the width of the collection view.
Is this possible to do with Flowlayout?
I've done it like this.
#define space 5
#import "MultpleLineLayout.h"
#implementation MultpleLineLayout { // a subclass of UICollectionViewFlowLayout
NSInteger itemWidth;
NSInteger itemHeight;
}
-(id)init {
if (self = [super init]) {
itemWidth = 60;
itemHeight = 60;
}
return self;
}
-(CGSize)collectionViewContentSize {
NSInteger xSize = [self.collectionView numberOfItemsInSection:0] * (itemWidth + space); // "space" is for spacing between cells.
NSInteger ySize = [self.collectionView numberOfSections] * (itemHeight + space);
return CGSizeMake(xSize, ySize);
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path {
UICollectionViewLayoutAttributes* attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:path];
attributes.size = CGSizeMake(itemWidth,itemHeight);
int xValue = itemWidth/2 + path.row * (itemWidth + space);
int yValue = itemHeight + path.section * (itemHeight + space);
attributes.center = CGPointMake(xValue, yValue);
return attributes;
}
-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect {
NSInteger minRow = (rect.origin.x > 0)? rect.origin.x/(itemWidth + space) : 0; // need to check because bounce gives negative values for x.
NSInteger maxRow = rect.size.width/(itemWidth + space) + minRow;
NSMutableArray* attributes = [NSMutableArray array];
for(NSInteger i=0 ; i < self.collectionView.numberOfSections; i++) {
for (NSInteger j=minRow ; j < maxRow; j++) {
NSIndexPath* indexPath = [NSIndexPath indexPathForItem:j inSection:i];
[attributes addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
}
}
return attributes;
}
My data source was an array of arrays with each inner array providing the data for an individual row.
After Edit:
My collection view scrolled in both directions. This is what I had in my viewDidload to set things up:
- (void)viewDidLoad {
self.theData = #[#[#"0",#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"11",#"12",#"13",#"14",#"15",#"16",#"17",#"18",#"19",#"20"], #[#"0",#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"11",#"12",#"13",#"14",#"15",#"16",#"17",#"18",#"19",#"20"],#[#"0",#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"11",#"12",#"13",#"14",#"15",#"16",#"17",#"18",#"19",#"20"],#[#"0",#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"11",#"12",#"13",#"14",#"15",#"16",#"17",#"18",#"19",#"20"],#[#"0",#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"11",#"12",#"13",#"14",#"15",#"16",#"17",#"18",#"19",#"20"],#[#"0",#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"11",#"12",#"13",#"14",#"15",#"16",#"17",#"18",#"19",#"20"],#[#"0",#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"11",#"12",#"13",#"14",#"15",#"16",#"17",#"18",#"19",#"20"],#[#"0",#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"11",#"12",#"13",#"14",#"15",#"16",#"17",#"18",#"19",#"20"],#[#"0",#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"11",#"12",#"13",#"14",#"15",#"16",#"17",#"18",#"19",#"20"],#[#"0",#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"11",#"12",#"13",#"14",#"15",#"16",#"17",#"18",#"19",#"20"],#[#"Z0",#"Z1",#"Z2",#"Z3",#"Z4",#"Z5",#"Z6",#"Z7",#"Z8",#"Z9",#"Z10",#"Z11",#"Z12",#"Z13",#"Z14",#"Z15",#"Z16",#"Z17",#"Z18",#"Z19",#"Z20"]];
MultpleLineLayout *layout = [[MultpleLineLayout alloc] init];
self.collectionView = [[RDCollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];
self.collectionView.dataSource = self;
self.collectionView.delegate = self;
self.view.backgroundColor = [UIColor blackColor];
[self.view addSubview:self.collectionView];
[self.collectionView registerClass:[DataCell class] forCellWithReuseIdentifier:#"DataCell"];
[self.collectionView reloadData];
}

Is there a way to make a UICollectionView row or header stay still a.k.a. freeze panes?

I'm wondering if there is a way to make a UICollectionView row or header stay still similar to the freeze panes function in a spreadsheet program.
I basically want the first column and row to stay still while the rest is pannable.
Is this possible to do with the UICollectionView?
I have found that the following code will set the first row/section as always visable and will scroll with the appropriate section/row:
- (NSArray *) layoutAttributesForElementsInRect: (CGRect) rect
{
NSMutableArray *attributes = [NSMutableArray array];
for (NSInteger section = 0; section < self.collectionView.numberOfSections; section++)
for (NSInteger item = 0 ; item < [self.collectionView numberOfItemsInSection: section]; item++)
{
UICollectionViewLayoutAttributes *layout = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:item inSection:section]];
if(section==0 || item==0)
{
UICollectionView * const cv = self.collectionView;
CGPoint const contentOffset = cv.contentOffset;
CGPoint origin = layout.frame.origin;
if(item==0)
{
origin.x = contentOffset.x;
layout.zIndex = 1022;
}
if(section==0)
{
origin.y = contentOffset.y;
layout.zIndex = 1023;
if(item==0)layout.zIndex = 1024;
}
layout.frame = (CGRect)
{
.origin = origin,
.size = layout.frame.size
};
}
[attributes addObject:layout];
}
return attributes;
}

Resources