I have a collection view with the layout set to horizontal scrolling. Each header section contains a label which needs to be rotated 90 degrees
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
UICollectionReusableView *reusableview = nil;
if (kind == UICollectionElementKindSectionHeader)
{
Collection *collection=self.collectionArray[indexPath.section];
UICollectionReusableView *headerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:#"HeaderView" forIndexPath:indexPath];
headerView.tag=indexPath.section;
headerView.backgroundColor=[Settings getInstance].textColor;
UIImageView* expandView=(UIImageView*)[headerView viewWithTag:kExpandImage];
expandView.image=[UIImage imageNamed:[NSString stringWithFormat:#"expand_%#.png",[Settings getInstance].appBrand]];
UILabel* label=(UILabel*)[headerView viewWithTag:kCollectionLabel];
label.text=[NSString stringWithFormat:#"%#",collection.name];
label.backgroundColor=[Settings getInstance].textColor;
label.textColor=[Settings getInstance].backColor;
label.font=[UIFont fontWithName:#"HelveticaNeue-UltraLight" size:50];
label.transform = CGAffineTransformMakeRotation(-M_PI_2);
CGRect frame=label.frame;
frame.size.height=headerView.layer.frame.size.height;
frame.origin.y=headerView.layer.frame.origin.y;
label.frame=frame;
//add tap gesture to detect touch
HeaderTapRecognizer *singleTapRecogniser = [[HeaderTapRecognizer alloc] initWithTarget:self action:#selector(sectionTapped:)];
singleTapRecogniser.sectionIndexPath=indexPath;
singleTapRecogniser.delegate=self;
singleTapRecogniser.numberOfTouchesRequired = 1;
singleTapRecogniser.numberOfTapsRequired = 1;
[headerView addGestureRecognizer:singleTapRecogniser];
reusableview= headerView;
}
if (kind == UICollectionElementKindSectionFooter) {
UICollectionReusableView *footerview = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:#"FooterView" forIndexPath:indexPath];
reusableview = footerview;
}
return reusableview;
}
When I run this through Instruments, I'm getting an increased memory allocation under GSEventRunModal. The problem is resolved by taking out this line of code
label.transform = CGAffineTransformMakeRotation(-M_PI_2);
All examples I've seen all use this method to rotate a label so unsure where I'm going wrong.
Any ideas are most welcome, thanks.
One thing to try would be applying the transformation BEFORE you add the text, background color, text color and font. That way you are only rotating an empty label, then subsequently filling it.
Related
I have a UILabel in a collectionView header. The label is set to zero lines, word wrapping, and proper leading/trailing/top space constraints. If i DO NOT call [collectionView reloadData], the label expands properly to text with greater than two lines. Once reloadData is called, the label goes back to a single line...the second line disappears.
- (UICollectionReusableView *) collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath{
if (kind == UICollectionElementKindSectionHeader) {
header = (viewRollHeader *) [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:#"header" forIndexPath:indexPath];
header.rollTitle.text = [self.roll objectForKey:#"title"];
header.rollDescription.text = [self.roll objectForKey:#"info"];
[header.cancelButton addTarget:self action:#selector(exit) forControlEvents:UIControlEventTouchUpInside];
return header;
}
return [UICollectionReusableView new];
}
- (CGSize) collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section{
if (section == 0) {
CGRect labelRect = [[self.roll objectForKey:#"title"]
boundingRectWithSize: header.rollTitle.frame.size
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName : [UIFont fontWithName:#"Arial-BoldMT" size:32.0f]}
context:nil];
return CGSizeMake([[UIScreen mainScreen]bounds].size.width, (174.0f + labelRect.size.height));
}
return CGSizeZero;
}
collectionView:viewForSupplementaryElementOfKind:atIndexPath: is used for the header or footer of the entire collectionView, while collectionView:referenceSizeForHeaderInSection: is used for the header of a specific section of the collectionView
to make sure the size of the header stays consistent and respects your constraints, you'll need to call [(UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout setHeaderReferenceSize:CGSizeMake(width, height)] once the flowlayout is instantiated
Use label setter property inside delegate method where you configure each cell.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cell" forIndexPath:indexPath];
//add label property here
return cell;
}
After reloading, it calls this method again to configure the label.
Try setting the preferredMaxLayoutWidth to the label
self.label.preferredMaxLayoutWidth = 300;
I'm create UICollectionView programmatically to uiview (i use single view). like this
UICollectionViewFlowLayout *CATLayout=[[UICollectionViewFlowLayout alloc]init];
CATLayout.minimumInteritemSpacing = 2.0f;
CATLayout.minimumLineSpacing = 2.0f;
CATLayout.scrollDirection = UICollectionViewScrollDirectionVertical;
CATLayout.sectionInset = UIEdgeInsetsMake(1.0f, 1.0f, 1.0f, 1.0f);
self.ColStickersListView=[[UICollectionView alloc]initWithFrame:CGRectMake(0, 0, StickersListView.frame.size.width, StickersListView.frame.size.height) collectionViewLayout:CATLayout];
self.ColStickersListView.delegate=self;
self.ColStickersListView.dataSource=self;
self.ColStickersListView.tag=2;
[self.ColStickersListView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:#"CollStickersList"];
[self.ColStickersListView setBackgroundColor:[UIColor clearColor]];
[StickersListView addSubview:ColStickersListView];
and
UICollectionViewCell *cell = [ColStickersListView dequeueReusableCellWithReuseIdentifier:#"CollStickersList" forIndexPath:indexPath];
// for background selected
NSString *imageName=anObject;
NSString *filename=[NSString stringWithFormat:#"%#/Stickers/List/%#",[appDel DocsPath],imageName];
NSLog(#"%#",filename);
cell.backgroundColor=[UIColor clearColor];
UIImage *image=[UIImage imageNamed:filename];
UIImageView *photoView=[[UIImageView alloc]initWithFrame:CGRectMake(StickersListPadding,StickersListPadding,StickersListThumbSize-(StickersListPadding*2),StickersListThumbSize-(StickersListPadding*2))];
photoView.image=image;
photoView.contentMode=UIViewContentModeScaleAspectFit;
photoView.userInteractionEnabled = YES;
[cell.contentView addSubview:photoView];
return cell;
it's work perfect for display image to cell.
Problem !!
if scroll page to bottom and scroll return to top again that image in cell it's overlap.
How to fix it.!!! (Programmatically only with single view)
I suspect because you aren't setting the frame on the UIImageView it is deriving its size from the image that is being set on it. So in some cases it might overflow its parent view -> the cell in this case.
I suggest you setup some constraints on the UIImageView, this can be done programmatically.
So try to set left, right, top and bottom constraints on the UIIMageView so that it stays within the bounds of the cell
During scroll
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath is beeing called repeatively..
the means your are adding UIImageView *photoView repeatedly to cell.contentView
UIImageView *photoView=[[UIImageView alloc]initWithFrame:CGRectMake(StickersListPadding,StickersListPadding,StickersListThumbSize-(StickersListPadding*2),StickersListThumbSize-(StickersListPadding*2))];
//codes..
[cell.contentView addSubview:photoView];
doing something like this prevent that:
UICollectionViewCell *cell = [ColStickersListView dequeueReusableCellWithReuseIdentifier:#"CollStickersList" forIndexPath:indexPath];
if (cell == nil)
{
UIImageView *photoView=[[UIImageView alloc]initWithFrame:CGRectMake(StickersListPadding,StickersListPadding,StickersListThumbSize-(StickersListPadding*2),StickersListThumbSize-(StickersListPadding*2))];
// codes...
[cell.contentView addSubview:photoView];
}
Another way of solving this is by creating a custom collection cell:
// CustomCollectionCell.h
#interface YourCollectionCell : UICollectionViewCell
#property (nonatomic) UIImageView *photoView;
#end
// CustomCollectionCell.m
#implementation YourCollectionCell
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// and setting up you imageview here
self.photoView=[[UIImageView alloc]initWithFrame:YourRect];
self.photoView.contentMode=UIViewContentModeScaleAspectFit;
self.photoView.userInteractionEnabled = YES;
[self.contentView addSubview:self.photoView];
}
return self;
}
and using it like:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CustomCollectionCell *cell = (CustomCollectionCell *)[ColStickersListView dequeueReusableCellWithReuseIdentifier:#"CollStickersList" forIndexPath:indexPath];
cell.photoView.image=image;
return cell;
}
Hope i've helped you, happy coding.. Cheers & Good night!
I have to implement a chess style grid view to show some radial progress bar.!
I made the grid using some inefficient algorithm to do this, since UiCollectionView don't use rows and columns, and it worked, but when the user scrolled the collection to the limit and the "row" of the collection disappear from the screen and the user release the screen and the UICollectionViewCells reappear they switched its colors.
I know the switching happens by the BOOLs, but i don't know how solve this.
This is my algorithm (i have 2 differents UICollectionViewCells, one for each color)
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = #"Cell";
static NSString *identifierPair = #"CellPair";
UICollectionViewCell *cell ;
UILabel *title;
//...Creating the circle progress bar....
if ((indexPath.item % 2) == 0) {
if (isPair) {
cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifierPair forIndexPath:indexPath];
isPair = NO;
title = (UILabel *)[cell viewWithTag:12];
}
else
{
cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
isPair = YES;
title = (UILabel *)[cell viewWithTag:11];
}
}
else {
if (isUneven) {
cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifierPair forIndexPath:indexPath];
isUneven = NO;
title = (UILabel *)[cell viewWithTag:12];
}
else
{
cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
isUneven = YES;
title = (UILabel *)[cell viewWithTag:11];
}
}
//...Setting the name to the cell....
return cell;
}
This seems more the case of using a single checkered background inside the UICollectionView, but since I'm not sure that it is doable you could do something like:
+ (BOOL)checkersCellAtIndexIsDark:(NSIndexPath *)indexPath {
NSInteger squareIndex = indexPath.item % 4;
return (squareIndex == 0 || squareIndex == 3);
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPath];
cell.backgroundColor = ([[self class] checkersCellAtIndexIsDark:indexPath])? [UIColor orangeColor] : [UIColor yellowColor];
// Set other things
return cell;
}
I don't understand why you use different reuseIdentifiers and titleLabels for different backgrounds since I don't notice from the screens, but you can always edit this code
You didn't have to use 2 different cells to change the background.
All you need is to set the default color of the background and when the indexPath.item is even, just set the other color for the cell background.
But is important to set the default color first, otherwise you will have the same result.
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.
Following code is a very simple program which is used to display numbers from 1-10000 in the UICollectionView. It is displaying correctly without scrolling, but the cells are overlapped if you scroll down and scroll back the collection view.
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return 10000;
}
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
return CGSizeMake(100,30);
}
-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
NSString *identifier = #"cell_id";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 30)];
[label setText:[NSString stringWithFormat:#"%d", indexPath.item, nil]];
[cell.contentView addSubview:label];
return cell;
}
The problem here is that the label is being added to the view's cell repeatedly. The old label is not removed when the cell is reused and hence you see multiple numbers overlapped. the solution can be to remove the old label like this
for (UIView *view in cell.contentView.subviews) {
if ([view isKindOfClass:[UILabel class]]) {
[view removeFromSuperview];
}
}
before adding to the cell. However this will create performance problems when the number of subviews increase. You can create a custom cell with a label and then update its value. I haven't tried it but i believe it will work. Hope this helps
This worked for me in Swift 3:
Almost same solution as approved solution, note that I used
cell.subViews
instead of
cell.contentView.subViews
What's worked for me is:
for view in cell.subviews {
view.removeFromSuperview()
}
Set the cell layer anchorPointZ to row index in cellForItemAtIndexPath: method
cell.layer.anchorPointZ = CGFloat(indexPath.row)