Similar functionality exists for UITableView. I can add a header to UITableView or for every section in that UITableView. Here the question is how to add header to UICollectionView programatically? Not to every section of UICollectionView.
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
if (kind == UICollectionElementKindSectionHeader) {
UICollectionReusableView *reusableview = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:#"HeaderView" forIndexPath:indexPath];
if (reusableview==nil) {
reusableview=[[UICollectionReusableView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
}
UILabel *label=[[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
label.text=[NSString stringWithFormat:#"Recipe Group #%li", indexPath.section + 1];
[reusableview addSubview:label];
return reusableview;
}
return nil;
}
Related
Hi i am beginner in ios and in my project have to create UICollectionView with adding UIHeader as like how we are adding UItableView Header but according to my code UIHeader is not adding for CollectionView please help me some one
my code:-
#import "ViewController.h"
#interface ViewController ()
{
UICollectionView *_collectionView;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UICollectionViewFlowLayout *layout=[[UICollectionViewFlowLayout alloc] init];
_collectionView = [[UICollectionView alloc]initWithFrame:CGRectMake(0, 10, 320, 480) collectionViewLayout:layout];
[_collectionView setDataSource:self];
[_collectionView setDelegate:self];
[_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:#"cellIdentifier"];
[_collectionView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:#"HeaderView"];
[_collectionView setBackgroundColor:[UIColor clearColor]];
self.view.backgroundColor = [UIColor whiteColor];
[self.view addSubview:_collectionView];
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return 15;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:#"cellIdentifier" forIndexPath:indexPath];
cell.backgroundColor=[UIColor greenColor];
return cell;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
return CGSizeMake(50, 50);
}
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{
UIEdgeInsets insets=UIEdgeInsetsMake(10, 10, 10, 10);
return insets;
}
-(UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath{
if (kind == UICollectionElementKindSectionHeader) {
UICollectionReusableView *reusableview = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:#"HeaderView" forIndexPath:indexPath];
reusableview.backgroundColor = [UIColor redColor];
if (reusableview==nil) {
reusableview=[[UICollectionReusableView alloc] initWithFrame:CGRectMake(0, 0, 320, 50)];
}
UILabel *label=[[UILabel alloc] initWithFrame:CGRectMake(0, 0, 50, 10)];
label.text= #"Hellow orld";
[reusableview addSubview:label];
return reusableview;
}
return nil;
}
#end
it may help you
self.collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, 320, self.view.frame.size.height) collectionViewLayout:flowlayout];
self.collectionView.contentInset = UIEdgeInsetsMake(50, 0, 0, 0);
UIView *headerView = [[UIView alloc]init];
headerView.frame = CGRectMake(0, -50, 320, 50);
[self.collectionView addSubview: headerView];
[self.view addSubview: _collectionView];
Maybe you need return the size of the header in
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: MyFlowLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: xx, height: xx)
}
I have a collectionView with the following relevant methods:
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView
viewForSupplementaryElementOfKind:(NSString *)kind
atIndexPath:(NSIndexPath *)indexPath {
if (kind == UICollectionElementKindSectionHeader) {
Scoreboard *thisScoreboard = [self.scoreboards objectAtIndex:indexPath.section];
ScoreboardReusableView *view = nil;
view = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader
withReuseIdentifier:#"scoreboardHeader"
forIndexPath:indexPath];
//view.backgroundColor = [UIColor yellowColor];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 20)];
//label.text = [NSString stringWithFormat:#"%# vs %#", thisScoreboard.awayteam, thisScoreboard.hometeam];
label.text = [NSString stringWithFormat:#"%ld", (long)indexPath.section];
[view addSubview:label];
return view;
}
return nil;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
{
return CGSizeMake(self.view.frame.size.width, 34);
}
However, the label text is overlapping. Here are two screenshots of the collectionView. Sections 0 and 1 (notice how the text does NOT overlap):
And sections 2 and 3 (the text overlaps here and on all subsequent section headers):
My header xib ScoreboardReusableView is COMPLETELY blank because no matter what I put in it (e.g., a UILabel or a UIImageView, it would not show up in the UICollectionView Header). Preferably I would design the header in a xib, but since that's not working I tried to do it programmatically, but now labels are overlapping.
Collection views re-use cells and headers. When a header is scrolled offscreen it is added to a queue, and will be re-used for the next header to be scrolled onscreen. That means your configuration code in collectionView:viewForSupplementaryElementOfKind: method will be run again on the same header view at different indexPaths. Since you're just adding a label every time your configuration code runs, they will stack one on top of the other.
The correct approach is to have a label property on the headerView, so that its text can be changed. But...
no matter what I put in it (e.g., a UILabel or a UIImageView), it would not show up
This is your real issue. Do you have autolayout enabled on the xib? If so be sure to add constraints for the label, otherwise it will have zero width and height, giving the effect you describe.
Swift 3 version, hope it helps
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
var reusableview: UICollectionReusableView?
reusableview = nil
reusableview = playersCollectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "collViewHeader", for: indexPath as IndexPath)
if kind == UICollectionElementKindSectionHeader {
//remove all previous subviews:
if let views = reusableview?.subviews {
for view in views {
view.removeFromSuperview()
}
}
//----------
if reusableview == nil {
reusableview = UICollectionReusableView.init(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: 50))
}
//init label
let label = UILabel.init(frame: CGRect(x: 10.0, y: 20.0, width: self.view.frame.width-20, height: 25.0)) //-20 from width to make it no go out of screen bounds
label.textColor = UIColor.blue //you may set your color here :)
label.adjustsFontSizeToFitWidth = true
label.text = "your text here"
reusableview?.addSubview(label)
}
return reusableview!
}
ScoreboardReusableView is blank but it was reusable. You must put UILabel into header xib and set text for it after that.
For example:
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
if (kind == UICollectionElementKindSectionHeader) {
Scoreboard *thisScoreboard = [self.scoreboards objectAtIndex:indexPath.section];
ScoreboardReusableView *view = nil;
view = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader
withReuseIdentifier:#"scoreboardHeader"
forIndexPath:indexPath];
view.label.text = [NSString stringWithFormat:#"%ld", (long)indexPath.section];
return view;
}
return nil;
}
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
UICollectionReusableView *reusableview=nil;
reusableview = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:#"HeaderView" forIndexPath:indexPath];
NSArray *viewsToRemove = [reusableview subviews];
for (UIView *v in viewsToRemove) {
[v removeFromSuperview];
}
if (reusableview==nil) {
reusableview=[[UICollectionReusableView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 30)];
}
reusableview.backgroundColor=[UIColor colorWithRed:234.0/255.0 green:234.0/255.0 blue:234.0/255.0 alpha:1.0];
UILabel *lblTitle=[[UILabel alloc] initWithFrame:CGRectMake(10, 0, [UIScreen mainScreen].bounds.size.width-90, 30)];
[lblTitle setFont:[UIFont fontWithName:#"Ubuntu-Light" size:12]];
lblTitle.textColor=HTAtHomeColor;
[reusableview addSubview:lblTitle];
UILabel *lblCount=[[UILabel alloc] initWithFrame:CGRectMake([UIScreen mainScreen].bounds.size.width-80, 0, 70, 30)];
lblCount.textAlignment=NSTextAlignmentRight;
lblCount.textColor=[UIColor blackColor];
[lblCount setFont:[UIFont fontWithName:#"Ubuntu-Light" size:12]];
lblCount.text=[NSString stringWithFormat:#"%d Section Header",indexPath.row];
[reusableview addSubview:lblCount];
return reusableview;
}
return nil;
}
I created a UIView with a UICollectionView, and that UIView is added on a UIViewController. UIViewController using the delegate (photoDidTapped) of that UIView to determine is something was clicked/tapped. First row, is okay, means whole cell area is tappable. Second row, horizontal half of that is clickable. 3rd row, almost 10% horizontal of the cell is clickable and so on.
Code:
- (UICollectionView *)collectionView
{
if (!_collectionView) {
UICollectionViewFlowLayout *flowLayout=[[UICollectionViewFlowLayout alloc] init];
flowLayout.itemSize = CGSizeMake(THUMB_DIMENSION, THUMB_DIMENSION);
[flowLayout setMinimumInteritemSpacing:0.0f];
[flowLayout setMinimumLineSpacing:PHOTO_MARGIN];
_collectionView = [[UICollectionView alloc] initWithFrame:self.frame collectionViewLayout:flowLayout];
_collectionView.frame = CGRectMake(0, 0, SCREEN_WIDTH, self.frame.size.height);
_collectionView.backgroundColor = [UIColor clearColor];
_collectionView.delegate = self;
_collectionView.dataSource = self;
_collectionView.allowsSelection = YES;
_collectionView.alwaysBounceVertical = YES;
_collectionView.scrollEnabled = NO;
[_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:#"Cell"];
}
return _collectionView;
}
- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section {
return (_photos.count > 9 ? 9 : _photos.count);
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath; {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPath];
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, cell.frame.size.width, cell.frame.size.height)];
imgView.clipsToBounds = YES;
imgView.backgroundColor = RGB(250, 250, 250);
if (_photos.count) {
PhotoDM *photo = [_photos objectAtIndex:indexPath.row];
[imgView setImageWithURL:[NSURL URLWithString:photo.largeLink]
placeholderImage:[UIImage imageNamed:#"placeholder"]];
}
[cell addSubview:imgView];
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"tapped: %ld", (long)indexPath.row);
if([delegate respondsToSelector:#selector(photoDidTapped:)])
[delegate performSelector:#selector(photoDidTapped:) withObject:[[NSNumber alloc] initWithInteger:indexPath.row] ];
}
Your code contains 2 problems.
1. If you create a subview, you should use self.bounds.
2. The view can be resized, after UICollectionView has been added. It means that you should configure autoresizingMask or NSLayoutConstrans. (UICollectionView can be out of view's bounds).
To debug this problem, you can create different background colors for the view, the collectionView, the viewController's view.
Hi i'm trying to customize my collectionviewcell depending on its indexPath but also if I set
if (indexPath.row == 0)
{
[cell addSubView: view];
}
the view appear random in some cells.
This is the code I'm using
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return 15;
}
// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:#"cellIdentifier" forIndexPath:indexPath];
NSInteger row = indexPath.row;
UIView *contentCell = [[UIView alloc] initWithFrame:cell.frame];
if (row == 0)
{
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(1.0f, 1.0f, 50.0f, 50.0f)];
label.text = #"Test";
[contentCell addSubview:label];
}
[cell addSubview:contentCell];
cell.backgroundColor=[UIColor colorWithPatternImage:[UIImage imageNamed:#"container"]];
return cell;
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"%d", indexPath.row);
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(414, 228);
}
You have to read about reusable cells.
you can avoid the problem for now by doing this.
if (row == 0)
{
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(1.0f, 1.0f, 50.0f, 50.0f)];
label.text = #"Test";
label.tag = 200;
[contentCell addSubview:label];
}else{
UIView* lbl = [contentCell viewWithTag:200];
if(lbl)
[lbl removeFromSuperView];
}
but this will affect the scrolling and memory performance, you can put the label in the cell by default && show/hide it in the if/else blocks
You are adding view several times as a subview. Cells are reused due to "dequeueReusableCellWithReuseIdentifier", so after you scroll up and down, you get back existing view, which already has subview added.
You should read more about reusing cells.
One way to avoid that behavior is to create all views when you create a cell and just show / hide them.
Otherwise create cells with different identifier for different rows - in your case for row 0.
I have a UICollectionView in one of my viewcontroller. My collection view uses a subclass of UICollectionViewLayout (custom) to layout the cells. First thing, as soon as I select Layout as Custom in dropdown on Storyboard, option to select supplementary views goes away.
I tried doing that programatically as shown below, but none of the delegate methods are getting called.
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
if (kind == UICollectionElementKindSectionHeader) {
UICollectionReusableView *reusableview = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:#"HeaderView" forIndexPath:indexPath];
if (reusableview==nil) {
reusableview=[[UICollectionReusableView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
}
UILabel *label=[[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
label.text=[NSString stringWithFormat:#"Recipe Group #%li", indexPath.section + 1];
[reusableview addSubview:label];
return reusableview;
}
return nil;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section {
CGSize headerSize = CGSizeMake(320, 44);
return headerSize;
}
In my viewDidLoad Method I have
[self.collectionView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:#"HeaderView"];
Can anyone point me where I'm messing up?
You're passing in the incorrect view kind.
Your line registering the class:
[self.collectionView registerClass:[UICollectionReusableView class]
forSupplementaryViewOfKind:UICollectionElementKindSectionFooter
withReuseIdentifier:#"HeaderView"];
Should be:
[self.collectionView registerClass:[UICollectionReusableView class]
forSupplementaryViewOfKind: UICollectionElementKindSectionHeader
withReuseIdentifier:#"HeaderView"];
Edit: Looks like your code is all using sectionFooter. Are you trying to programmatically add a header or a footer?
Found the issue, I was not returning attributes of my header in this UICollectionLayoutView method:
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect; // return an array layout attributes instances for all the views in the given rect
Check that you gave reference size for header in UICollectionViewFlowLayout
[flowLayout setHeaderReferenceSize:CGSizeMake(320, 50)];
and for footer
[flowLayout setFooterReferenceSize:CGSizeMake(320, 50)];
Your delegate method for header reference size is wrong, you call footer method,referenceSizeForFooterInSection, as follow:
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section {
CGSize headerSize = CGSizeMake(320, 44);
return headerSize;
}
Individually set HeaderReferenceSize will fix the header problem. But app will crash you keep the above method and return nil in viewForSupplementaryElementOfKind for Footer.
your check:
if (kind == UICollectionElementKindSectionFooter)
Your should check for: UICollectionElementKindSectionHeader
same for:
dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter
I think you should call this in your viewdidload method:
[collectionViewFlowLayout setHeaderReferenceSize:CGSizeMake(self.collectionView.frame.size.width, 50)];