viewForSupplementaryElementOfKind not being called on custom UICollectionViewLayout - ios

I created a custom UICollectionViewLayout and I've put it in my Collection View Controller in the storyboard. I didn't get that much problems on displaying the items/cells. My problem is that the viewForSupplementaryElementOfKind is not being called. I can't to seem to pinpoint on why is that. I tried to go back to the default layout and it does call it then but I need my custom UICollectionViewLayout. I've left NSLogs to check and viewForSupplementaryElementOfKind is not being called.
Here is my MyCollectionViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
viewArray = [NSMutableArray array];
//setup the delegate for vertical flow layout
[(VerticalFlowLayout*)self.collectionViewLayout setDelegate:self];
//register the headers
[self.collectionView registerNib:[ButtonHeaderCollectionReusableView nib] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:[ButtonHeaderCollectionReusableView reusableID]];
//set the insets
[self.collectionView setContentInset:UIEdgeInsetsMake(23, 5, 10, 5)];
}
#pragma mark - CollectionView DataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return [self.cardsArray count];
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
NSLog(#"Number of Sections");
return 1;
}
- (UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"Making Card");
CardCell *card = [collectionView dequeueReusableCellWithReuseIdentifier:#"CardCell" forIndexPath:indexPath];
// UILabel *cardLabel = [card viewWithTag:101];
// [cardLabel setText:[textArray objectAtIndex:[indexPath row]]];
return card;
}
- (UICollectionReusableView*)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
NSLog(#"Making Header");
UICollectionReusableView *reusableView = nil; /*[[UICollectionReusableView alloc] initWithFrame:CGRectMake(0, 0, 100, 75)];*/
// [reusableView setBackgroundColor:[UIColor blackColor]];
if (kind == UICollectionElementKindSectionHeader) {
ButtonHeaderCollectionReusableView *headerview = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:[ButtonHeaderCollectionReusableView reusableID] forIndexPath:indexPath];
return headerview;
}
// if (kind == UICollectionElementKindSectionFooter) {
// UICollectionReusableView *footerview = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:#"FooterView" forIndexPath:indexPath];
// }
return reusableView;
}
- (CGSize)collectionView:collectionView layout:(nonnull UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
return CGSizeMake(180.0f, 72.0f);
}
Now here is my MyFlowLayout.m
- (id)initWithCoder:(NSCoder *)aDecoder {
NSLog(#"Vertical Flow Layout init with coder");
if(self = [super initWithCoder:aDecoder]) {
contentHeight = 0;
cachedAttributes = [NSMutableArray array];
}
return self;
}
- (void)prepareLayout {
NSLog(#"Preparing Layout");
// [super prepareLayout];
contentWidth = CGRectGetWidth(self.collectionView.bounds) - (self.collectionView.contentInset.left + self.collectionView.contentInset.right);
if([cachedAttributes count] == 0) {
//compute for column width
CGFloat columnWidth = contentWidth / NUMBER_OF_COLUMNS;
NSMutableArray *xOffsets = [NSMutableArray array];
for(int i = 0; i < NUMBER_OF_COLUMNS; i++) {
[xOffsets addObject:[NSNumber numberWithFloat:(i * columnWidth)]];
}
//compute for height
NSMutableArray *yOffsets = [NSMutableArray array];
for(int i = 0; i < NUMBER_OF_COLUMNS; i++) {
[yOffsets addObject:[NSNumber numberWithFloat:75]];
}
int column = 0;
//loop through all the sections and items in the collectionview
for(int i = 0; i < self.collectionView.numberOfSections; i++) {
for(int j = 0; j < [self.collectionView numberOfItemsInSection:i]; j++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:j inSection:i];
//time to do some frame calculation
///let's start with the width
CGFloat width = columnWidth - CELL_PADDING * 2;
///then the height
CGFloat viewHeight = [self.delegate getViewHeightWithCollectionView:self.collectionView indexPath:indexPath withWidth:width];
CGFloat height = CELL_PADDING + viewHeight + /*textHeight +*/ CELL_PADDING;
CGFloat xOffset = [[xOffsets objectAtIndex:column] floatValue];
CGFloat yOffset = [[yOffsets objectAtIndex:column] floatValue];
CGRect frame = CGRectMake(xOffset, yOffset, columnWidth, height);
CGRect insetFrame = CGRectInset(frame, CELL_PADDING, CELL_PADDING);
//now that computation is done we shall make the attributes
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
[attributes setFrame:insetFrame];
[cachedAttributes addObject:attributes];
//time to increment the height and the column
contentHeight = MAX(contentHeight, CGRectGetMaxY(frame));
NSNumber *yOffSetColumn = [yOffsets objectAtIndex:column];
yOffSetColumn = [NSNumber numberWithFloat:([yOffSetColumn floatValue] + height)];
[yOffsets replaceObjectAtIndex:column withObject:yOffSetColumn];
NSLog(#"Content Height: %f yOffSetColumn: %# == %#", contentHeight, yOffSetColumn, [yOffsets objectAtIndex:column]);
column = column >= (NUMBER_OF_COLUMNS - 1) ? 0 : ++column;
}
}
}
}
- (CGSize)collectionViewContentSize {
// NSLog(#"Collection View Content Size");
return CGSizeMake(contentWidth, contentHeight + 75);
}
// called after preparelayout to determine which items are visible in the given rect
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
NSMutableArray<UICollectionViewLayoutAttributes *> *layoutAttributes = [NSMutableArray array];
NSInteger sectionsCount = [self.collectionView.dataSource numberOfSectionsInCollectionView:self.collectionView];
NSLog(#"Sections count: %ld", (long)sectionsCount);
for(int i = 0; i < sectionsCount; i++) {
//for header
UICollectionViewLayoutAttributes *headerAttributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:i]];
// if(CGRectIntersectsRect(headerAttributes.frame, rect)) {
// NSLog(#"Adding Section Attribute");
[layoutAttributes addObject:headerAttributes];
// }
for (UICollectionViewLayoutAttributes *attributes in cachedAttributes) {
if(CGRectIntersectsRect(attributes.frame, rect)) {
[layoutAttributes addObject:attributes];
}
}
// NSInteger itemsCount = [self.collectionView numberOfItemsInSection:i];
// for(int j = 0; j < itemsCount; j++) {
// UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:j inSection:i]];
// if(CGRectIntersectsRect(attributes.frame, rect)) {
// [layoutAttributes addObject:attributes];
// }
// }
}
return layoutAttributes;
}
//- (UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
// NSLog(#"Layout Attributes For Item: %ld %ld", [indexPath row], [indexPath section]);
//
// UICollectionViewLayoutAttributes *attributes = [cachedAttributes objectAtIndex:[indexPath row]];
// return attributes;
//}
- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath {
NSLog(#"Layout Attributes For Header: %# %ld %ld", elementKind, [indexPath row], [indexPath section]);
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
[attributes setFrame:CGRectMake(0, 0, contentWidth /*320*/, 75.0f)];
return attributes;
}
As you can see in MyFlowLayout, there are things I've tried but still it didn't call viewForSupplementaryElementOfKind. I tried implementing layoutAttributesForItemAtIndexPath since I have layoutAttributesForSupplementaryViewOfKind. I tried also using fixed numbers like the 75 and 320 that you see just to check if the header would be generated. The delegate that you see, it's just for getting a height of a view.
Since I've given the header enough space to be seen and left NSLogs all over and yet (UICollectionReusableView*)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath is not being called.
Here are some that I've looked and tried: stackoverflow but as you can see from the commented code that I've included. It still didn't work.
Thanks for the help.

I also had a problem with this and my solution was to simply add attributes for section in prepareLayout
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader withIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]];
attributes.frame = CGRectMake(0, 0, self.collectionView.frame.size.width, 100);
[cachedAttributes addObject:attributes];

Add the default size of header section:
collectionViewLayout.headerReferenceSize = CGSize(width, height)
The default size values are (0, 0). Thats why header is not displaying.
OR
There is also another way for it, implement the delegate method for UICollectionView:
collectionView(_:layout:referenceSizeForFooterInSection:)

Related

UICollectionView: custom FlowLayout crash if empty sections

I have a problem using custom flow layout, if my sections are empty i got a "EXC_ARTHMETIC(code=EXC_I386_DIV, subcode=0x0)".
I'm using custom FlowLayout to add custom header and custom section background(the crash is related to the background).
some code (please look at layoutAttributesForBackgroundAtSection: to see where the crash happens):
// UIViewController
-(void)loadView
{
[super loadView];
collectionViewFlowLayout *flowLayout = [[collectionViewFlowLayout alloc]init];
flowLayout.sectionInset = UIEdgeInsetsMake(35, 20, 0, 20);
[flowLayout setItemSize:cellSize];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
flowLayout.minimumInteritemSpacing = 0.0f;
self.collectionView = [[UICollectionView alloc]initWithFrame:(CGRectZero) collectionViewLayout:flowLayout];
[self.collectionView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.collectionView setBackgroundColor:[UIColor colorWithRed:246./256. green:246./256. blue:246./256. alpha:1]];
[self.view addSubview:self.collectionView];
[[self.collectionView.topAnchor constraintEqualToAnchor:self.view.topAnchor] setActive:YES];
[[self.collectionView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor] setActive:YES];
[[self.collectionView.leftAnchor constraintEqualToAnchor:self.view.leftAnchor] setActive:YES];
[[self.collectionView.rightAnchor constraintEqualToAnchor:self.view.rightAnchor] setActive:YES];
[self.collectionView registerClass:[collectionViewCell class] forCellWithReuseIdentifier:reuseCellIdentifier];
[self.collectionView registerClass:[sectionHeaderReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:reuseHeaderIdentifier];
[self.collectionView registerClass:[sectionBackgroundReusableView class]
forSupplementaryViewOfKind:KindSectionBackground
withReuseIdentifier:NSStringFromClass([sectionBackgroundReusableView class])];
[self.collectionView setShowsHorizontalScrollIndicator:NO];
[self.collectionView setShowsVerticalScrollIndicator:YES];
[self.collectionView setDataSource:self];
[self.collectionView setDelegate:self];
}
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 6;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return 0;
}
-(UICollectionReusableView *)collectionView:(UICollectionView *)collectionV
viewForSupplementaryElementOfKind:(NSString *)kind
atIndexPath:(NSIndexPath *)indexPath
{
UICollectionReusableView *reusableview = nil;
if (kind == UICollectionElementKindSectionHeader)
{
reusableview = [collectionV dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:reuseHeaderIdentifier forIndexPath:indexPath];
NSString *sectionName = self.shelves[indexPath.section];
[(sectionHeaderReusableView*)reusableview configureWithTitle:sectionName];
}
if (kind == UICollectionElementKindSectionFooter)
{
}
if (kind == KindSectionBackground)
{
reusableview = [collectionView dequeueReusableSupplementaryViewOfKind:sectionBackground
withReuseIdentifier:NSStringFromClass([sectionBackgroundReusableView class])
forIndexPath:indexPath];
}
return reusableview;
}
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
{
return = KHeaderSize;
}
// FloawLayout subclass
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSMutableArray *attributes = [NSMutableArray arrayWithArray:[super layoutAttributesForElementsInRect:rect]];
// 1. get visible sections
NSInteger lastIndex = -1;
for(UICollectionViewLayoutAttributes * attr in attributes) {
lastIndex = attr.indexPath.section;
UICollectionViewLayoutAttributes * attr = [self layoutAttributesForBackgroundAtSection:lastIndex];
[attributes addObject:attr];
}
return attributes;
}
-(UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)kind
atIndexPath:(NSIndexPath *)indexPath
{
if([kind isEqualToString:KindSectionBackground]) {
return [self layoutAttributesForBackgroundAtSection:indexPath.section];
} else {
return [super layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:indexPath];
}
}
-(UICollectionViewLayoutAttributes *)layoutAttributesForBackgroundAtSection:(NSUInteger)section
{
NSIndexPath * indexPath =[NSIndexPath indexPathForItem:0
inSection:section];
UICollectionViewLayoutAttributes * attr = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:KindSectionBackground
withIndexPath:indexPath];
attr.hidden = NO;
attr.zIndex = -1;
// crash happen in the line below.
UICollectionViewLayoutAttributes * firstAttr = [selflayoutAttributesForItemAtIndexPath:indexPath];
CGRect frame;
frame.origin.x = firstAttr.frame.origin.x - self.sectionInset.left;
frame.origin.y = firstAttr.frame.origin.y - self.sectionInset.top;
frame.size.width = self.collectionView.bounds.size.width;
NSUInteger numItems = [self.collectionView numberOfItemsInSection:section];
CGFloat cellsPerLine = floorf(self.collectionView.bounds.size.width / self.itemSize.width);
NSUInteger numLines = ceilf(numItems / (float)cellsPerLine);
frame.size.height = numLines * firstAttr.size.height + (numLines-1)*self.minimumLineSpacing +
self.sectionInset.top + self.sectionInset.bottom;
attr.frame = frame;
return attr;
}
Could you please help to fix this, thank you.
Looks like layoutAttributesForItemAtIndexPath crashes when the section provided in the IndexPath is empty. This is probably a bug since it's supposed to return an optional (at least in swift). I had the same kind of issue and fixed it by checking the number of items in the section (swift example for section 1):
if dataSource.collectionView(collectionView, numberOfItemsInSection: 1) > 0 {
if let layoutAttributes = self.layoutAttributesForItem(at: IndexPath(row: 0, section: 1)) {
// update layoutAttributes or create your background attributes
}
}
One workaround would be to call collectionView.reloadData() in advance before we call any layout methods, this work for me.

Dynamic UICollectionView content size height is too long

I need to create a UICollectionView cells with dynamic heights according to each cell's image view height. I have created a subclass from UICollectionViewFlowLayout to implement the desired behaviour as follows:
#define numColumns 2
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
NSArray* attributesToReturn = [super layoutAttributesForElementsInRect:rect];
for (UICollectionViewLayoutAttributes* attributes in attributesToReturn) {
if (attributes.representedElementKind == nil) {
NSIndexPath* indexPath = attributes.indexPath;
attributes.frame = [self layoutAttributesForItemAtIndexPath:indexPath].frame;
}
}
return attributesToReturn;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewLayoutAttributes* currentItemAttributes = [super layoutAttributesForItemAtIndexPath:indexPath];
if (indexPath.item < numColumns) {
CGRect frame = currentItemAttributes.frame;
frame.origin.y = 5;
currentItemAttributes.frame = frame;
return currentItemAttributes;
}
NSIndexPath* indexPathPrev = [NSIndexPath indexPathForItem:indexPath.item-numColumns inSection:indexPath.section];
CGRect framePrev = [self layoutAttributesForItemAtIndexPath:indexPathPrev].frame;
CGFloat YPointNew = framePrev.origin.y + framePrev.size.height + 10;
CGRect frame = currentItemAttributes.frame;
frame.origin.y = YPointNew;
currentItemAttributes.frame = frame;
return currentItemAttributes;
}
And in the collection view controller delegate flow layout
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
float imageHeight = [[(Product *)self.products[indexPath.row] image] height];
float imageWidth = [[(Product *)self.products[indexPath.row] image] width];
float ratio = 150.0/imageWidth;
return CGSizeMake(150, ratio*imageHeight);
}
Cell implementation
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
ProductCell *cell = (ProductCell *)[self.collectionView dequeueReusableCellWithReuseIdentifier:#"ProductCell" forIndexPath:indexPath];
....
[cell.imageView setImageWithURL:[NSURL URLWithString:[[(Product *)self.products[indexPath.row] image] url]]];
return cell;
}
All the cells showed up as expected, but the content view was too long as shown.
I think I have to implement the method -(CGSize)collectionViewContentSize to get the correct contentSize, but I don't know how to calculate the actual height of the collection view

First row Top spacing UICollectioViewCell

i'm having problems to align the first row to the top when I have different UICollectionViewCell heights and I don't know why. It seems that minimumLineSpacingForSectionAtIndex method is not working.
Here is my result:
As you can see, the first row is not aligned to the top.
This is my code:
- (void)viewDidLoad
{
...
FlowLayout *flowLayout = [[FlowLayout alloc] init];
flowLayout.itemSize = CGSizeMake(152, 260);
flowLayout.sectionInset = UIEdgeInsetsMake( 5, 5, 5, 5);
flowLayout.scrollDirection = UICollectionViewScrollDirectionVertical;
self.collectionView.collectionViewLayout = flowLayout;
...
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:
(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)
indexPath{
if (indexPath.row == 0)
return CGSizeMake(152, 200);
if (indexPath.row == 1)
return CGSizeMake(152, 270);
return CGSizeMake(152, 300);
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:
(UICollectionViewLayout*)collectionViewLayout
minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
return 5.0;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout*)collectionViewLayout
minimumLineSpacingForSectionAtIndex:(NSInteger)section {
return 8.0;
}
This is the code of my FlowLayout:
#implementation FlowLayout
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSMutableArray *result = [NSMutableArray array];
NSInteger sectionCount = 1;
if ([self.collectionView.dataSource respondsToSelector:#selector(numberOfSectionsInCollectionView:)])
{
sectionCount = [self.collectionView.dataSource numberOfSectionsInCollectionView:self.collectionView];
}
for (int s = 0; s < sectionCount; s++)
{
NSInteger itemCount = [self.collectionView.dataSource collectionView:self.collectionView numberOfItemsInSection:s];
for (int i = 0; i < itemCount; i++)
{
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:s];
UICollectionViewLayoutAttributes *layoutAttributes =
[self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath];
if (layoutAttributes != nil) {
[result addObject:layoutAttributes];
}
UICollectionViewLayoutAttributes *layout = [self layoutAttributesForItemAtIndexPath:indexPath];
if (CGRectIntersectsRect(rect, layout.frame))
{
[result addObject:layout];
}
}
}
return result;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewLayoutAttributes* atts =
[super layoutAttributesForItemAtIndexPath:indexPath];
if (indexPath.item == 0 || indexPath.item == 1) // degenerate case 1, first item of section
return atts;
NSIndexPath* ipPrev =
[NSIndexPath indexPathForItem:indexPath.item-2 inSection:indexPath.section];
CGRect fPrev = [self layoutAttributesForItemAtIndexPath:ipPrev].frame;
CGFloat rightPrev = fPrev.origin.y + fPrev.size.height + 10;
if (atts.frame.origin.y <= rightPrev) // degenerate case 2, first item of line
return atts;
CGRect f = atts.frame;
f.origin.y = rightPrev;
atts.frame = f;
return atts;
}
- (BOOL) shouldInvalidateLayoutForBoundsChange:(CGRect)newBound {
return YES;
}
What you are experiencing is a normal cell placement when using the UICollectionViewFlowLayout.
You have to tweak the layout attributes being returned by the flow layout. To do so, override the layoutAttributesForElementsInRect: method and any of the methods that return layout attributes. Modify the attributes provided by the parent class, and then return them. The attribute you have to modify is frame.
A good example of doing it is a sample code from Apple - Custom Layouts: A Worked Example
Update after attaching implementation of the layout.
Looks like in the layoutAttributesForItemAtIndexPath: method you are not adjusting the frame for first item (index 0). You are returning the attributes you get from parent class. Change the frame's origin.y to 0 for item 0.

How to make sticky headers in UICollectionView on iOS7?

I'm using this solution to make sticky headers in collection view and it works well on iOS6. But in iOS7 if the "Extend edges under top bars" option is checked then the header sticks to the origin of the view which is then behind the navigation bar.
So for now I disabled that option but I'd really like to tweak that solution to make it work when the edges are extended (in order to have the nice blurry effect when the content scrolls behind the navigation bar).
Can someone help me with that? Or does someone have any other suggestions to accomplish the same goal?
Here is my full solution. Just add the following to your FlowLayout class:
#pragma mark STICKY HEADERS CODE BELOW
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
NSMutableArray *answer = [[super layoutAttributesForElementsInRect:rect] mutableCopy];
NSMutableIndexSet *missingSections = [NSMutableIndexSet indexSet];
for (NSUInteger idx=0; idx<[answer count]; idx++) {
UICollectionViewLayoutAttributes *layoutAttributes = answer[idx];
if (layoutAttributes.representedElementCategory == UICollectionElementCategoryCell || layoutAttributes.representedElementCategory == UICollectionElementCategorySupplementaryView) {
[missingSections addIndex:(NSUInteger) layoutAttributes.indexPath.section]; // remember that we need to layout header for this section
}
if ([layoutAttributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) {
[answer removeObjectAtIndex:idx]; // remove layout of header done by our super, we will do it right later
idx--;
}
}
// layout all headers needed for the rect using self code
[missingSections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:idx];
UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath];
if (layoutAttributes) {
[answer addObject:layoutAttributes];
}
}];
return answer;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
UICollectionViewLayoutAttributes *attributes = [super layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:indexPath];
if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
UICollectionView * const cv = self.collectionView;
CGFloat topOffset = 0;
if ([self.collectionView.dataSource isKindOfClass:[UIViewController class]]) {
UIViewController *collectionViewParentViewController = (UIViewController *)self.collectionView.dataSource;
topOffset = collectionViewParentViewController.topLayoutGuide.length;
}
CGPoint const contentOffset = CGPointMake(cv.contentOffset.x, cv.contentOffset.y + topOffset);
CGPoint nextHeaderOrigin = CGPointMake(INFINITY, INFINITY);
if (indexPath.section+1 < [cv numberOfSections]) {
UICollectionViewLayoutAttributes *nextHeaderAttributes = [super layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:[NSIndexPath indexPathForItem:0 inSection:indexPath.section+1]];
nextHeaderOrigin = nextHeaderAttributes.frame.origin;
}
CGRect frame = attributes.frame;
if (self.scrollDirection == UICollectionViewScrollDirectionVertical) {
frame.origin.y = MIN(MAX(contentOffset.y, frame.origin.y), nextHeaderOrigin.y - CGRectGetHeight(frame));
}
else {
frame.origin.x = MIN(MAX(contentOffset.x, frame.origin.x), nextHeaderOrigin.x - CGRectGetWidth(frame));
}
attributes.zIndex = 1024;
attributes.frame = frame;
}
return attributes;
}
- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:indexPath];
return attributes;
}
- (UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:indexPath];
return attributes;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBound {
return YES;
}

Custom UICollectionViewLayout Different Cell Sizes

I want to make custom UICollectionViewLayout like this.
http://i.stack.imgur.com/aWUIa.jpg
When I scroll second page image size not fit the cell sizes;
http://i.stack.imgur.com/GOUmF.jpg
And turn back first page again;
http://i.stack.imgur.com/lQQ3h.png
Image sizes and cell sizes not fit each other.
Here my custom layout code;
//
// SeyahatLayout.m
// SeyahatTrend
//
// Created by Mehmet Can Yavuz on 03.06.2013.
// Copyright (c) 2013 Mehmet Can Yavuz. All rights reserved.
//
#import "SeyahatLayout.h"
#import "SeyahatLayoutAttributes.h"
#define kNumberOfItemsPerPage 5
static NSString * const BHPhotoAlbumLayoutPhotoCellKind = #"PhotoCell";
#interface SeyahatLayout ()
#property (nonatomic, strong) NSDictionary *layoutInfo;
#end
#implementation SeyahatLayout
- (CGSize)collectionViewContentSize
{
// Ask the data source how many items there are (assume a single section)
id<UICollectionViewDataSource> dataSource = self.collectionView.dataSource;
int numberOfItems = [dataSource collectionView:self.collectionView
numberOfItemsInSection:0];
// Determine how many pages are needed
int numberOfPages = ceil((float)numberOfItems/(float)kNumberOfItemsPerPage);
// Set the size
float pageWidth = self.collectionView.frame.size.width;
float pageHeight = self.collectionView.frame.size.height;
CGSize contentSize = CGSizeMake(numberOfPages * pageWidth, pageHeight);
return contentSize;
}
#pragma mark - Layout
- (void)prepareLayout
{
NSMutableDictionary *newLayoutInfo = [NSMutableDictionary dictionary];
NSMutableDictionary *cellLayoutInfo = [NSMutableDictionary dictionary];
NSInteger sectionCount = [self.collectionView numberOfSections];
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
for (NSInteger section = 0; section < sectionCount; section++) {
NSInteger itemCount =
[self.collectionView numberOfItemsInSection:section];
for (NSInteger item = 0; item < itemCount; item++) {
indexPath = [NSIndexPath indexPathForItem:item inSection:section];
SeyahatLayoutAttributes* attr =
[self layoutAttributesForItemAtIndex:item];
// UICollectionViewLayoutAttributes *itemAttributes =
// [UICollectionViewLayoutAttributes
// layoutAttributesForCellWithIndexPath:indexPath];
// itemAttributes.frame = [self frameForAlbumPhotoAtIndexPath:indexPath];
[cellLayoutInfo setObject:attr forKey:indexPath];
}
}
newLayoutInfo[BHPhotoAlbumLayoutPhotoCellKind] = cellLayoutInfo;
self.layoutInfo = newLayoutInfo;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:
(NSIndexPath *)indexPath {
return self.layoutInfo[BHPhotoAlbumLayoutPhotoCellKind][indexPath];
}
-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect
{
// NSInteger itemCount = [self.collectionView numberOfItemsInSection:0];
// NSMutableArray* attributes = [NSMutableArray array];
// for (NSInteger i=0 ; i < itemCount; i++) {
// NSIndexPath* indexPath = [NSIndexPath indexPathForItem:i
inSection:0];
// [attributes addObject:
[self layoutAttributesForItemAtIndexPath:indexPath]];
// }
// return attributes;
NSLog(#"RECTX: %f",rect.origin.x );
NSLog(#"RECTY: %f",rect.origin.y );
NSLog(#"RECTWIDTH: %f",rect.size.width );
NSLog(#"RECTHEIGHT: %f",rect.size.height );
id<UICollectionViewDataSource> dataSource = self.collectionView.dataSource;
int numberOfItems = [dataSource collectionView:self.collectionView
numberOfItemsInSection:0];
NSMutableArray* attributes = [NSMutableArray array];
int page = rect.origin.x / 1024.0;
if(numberOfItems>0){
for(NSInteger i = 0; i<kNumberOfItemsPerPage; i++){
int x = page*kNumberOfItemsPerPage + i;
NSLog(#"%d",x);
NSIndexPath* indexPath = [NSIndexPath indexPathForItem:x inSection:0];
[attributes addObject:self.layoutInfo[BHPhotoAlbumLayoutPhotoCellKind]
[indexPath]];
}
return attributes;
}
else
return nil;
}
- (SeyahatLayoutAttributes *)layoutAttributesForItemAtIndex:(int)index
{
NSIndexPath *path = [NSIndexPath indexPathForItem:index inSection:0];
SeyahatLayoutAttributes *attributes =
(SeyahatLayoutAttributes *)[UICollectionViewLayoutAttributes
layoutAttributesForCellWithIndexPath:path];
// Figure out what page this item is on
int pageNumber = floor((float)index / (float) kNumberOfItemsPerPage);
// Set the horizontal offset for the start of the page
float pageWidth = self.collectionView.frame.size.width;
float horizontalOffset = pageNumber * pageWidth;
// Now, determine which position this cell occupies on the page.
int indexOnPage = index % kNumberOfItemsPerPage;
int column = 0;
switch (indexOnPage) {
case 0:
case 1:
column = 0;
break;
case 2:
case 3:
case 4:
column = 3;
break;
default:
column = 0;
break;
}
int row = 0;
switch (indexOnPage) {
case 0:
case 2:
row = 0;
break;
case 3:
row = 1;
break;
case 1:
case 4:
row = 2;
break;
default:
row = 0;
break;
}
horizontalOffset = horizontalOffset + ((175.0 + 30.0) * column) + 30.0;
float verticalOffset = (194.0 + 30.0) * row + 30.0;
// finally, determine the size of the cell.
float width = 0.0;
float height = 0.0;
switch (indexOnPage) {
case 0:
width = 585.0;
height = 418.0;
break;
case 1:
width = 585.0;
height = 194.0;
break;
default:
width = 350.0;
height = 194.0;
break;
}
CGRect frame = CGRectMake(horizontalOffset, verticalOffset, width, height);
[attributes setFrame:frame];
return attributes;
}
+ (Class)layoutAttributesClass
{
return [SeyahatLayoutAttributes class];
}
#end
Here my custom uicollectionviewcell code;
//
// SeyahatCell.m
// SeyahatTrend
//
// Created by Mehmet Can Yavuz on 03.06.2013.
// Copyright (c) 2013 Mehmet Can Yavuz. All rights reserved.
//
#import "SeyahatCell.h"
#import "PNCollectionCellBackgroundView.h"
#implementation SeyahatCell
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
//self.backgroundColor = [UIColor whiteColor];
PNCollectionCellBackgroundView* backgroundView =
[[PNCollectionCellBackgroundView alloc] initWithFrame:frame];
self.backgroundView = backgroundView;
self.imageView =
[[UIImageView alloc] initWithFrame:CGRectMake(5,
5,
frame.size.width-10,
frame.size.height-10)];
[self.contentView addSubview:self.imageView];
}
return self;
}
And my cellForItemAtIndexPath method;
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
SeyahatCell *seyahatCell =
[collectionView dequeueReusableCellWithReuseIdentifier:SeyahatCellIdentifier
forIndexPath:indexPath];
Post * p = [self.posts objectAtIndex:indexPath.row];
[seyahatCell.imageView setImage:nil];
if(p.thumbnail){
[seyahatCell.imageView setImageWithURL:[NSURL URLWithString:p.thumbnail]];
}
else{
[seyahatCell.imageView setImage:nil];
}
return seyahatCell;
}
How can i fix this problem?
Try to use existing solutions for layouts. For this purpose i prefer to use https://github.com/bryceredd/RFQuiltLayout

Resources