I implemented UICollection view cell with Auto Layout. It works fine.
But my cell height is dynamic so cell should be change its height as per constraint set.
but height is not changed in any case.
My Code
- (void)viewDidLoad {
[super viewDidLoad];
UICollectionViewFlowLayout *flowlayout = (UICollectionViewFlowLayout*)_col.collectionViewLayout;
flowlayout.estimatedItemSize = CGSizeMake(self.view.frame.size.width - 30, 600);
}
-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
FirstCell *cell = [_col dequeueReusableCellWithReuseIdentifier:#"FirstCell" forIndexPath:indexPath];
return cell;
}
Storyboard
Simulator
All labels are multi lined
Help me to solve this
thanks for your time
Please follow below thing to achieve self sizing collection cell.
1) Same as table cells, use constraints or override -sizeThatFits:.
2) Override -[UICollectionReusableView preferredLayoutAttributesFittingAttributes:] to adjust layout attributes determined by the UICollectionViewLayout.
3) New estimatedItemSize property on UICollectionViewFlowLayout, equivalent to estimatedRowHeight on UITableView.
There are no self-sizing collection view cells in iOS before iOS 10. (Apple has claimed in WWDC videos that this feature exists, but it doesn't.)
You have to implement the delegate method collectionView:layout:sizeForItemAtIndexPath to provide a size for each cell. It can call systemLayoutSizeFittingSize: to use the internal constraints to do that, if you like; but the runtime will not magically do it for you.
Without constraints my UICollectionViewCells load immediately without any problems at all. When I put constraints on in storyboard so the image view is horizontal and vertically centered to the cell, the first image cell does not load. I have to scroll a few times and then back to the beginning for the first image cell to show. Take the constraints off and it goes back to working perfectly fine.
I thought maybe the images weren't getting loaded in time but that doesn't appear to be the case.
What am I missing?
I can put in some code but it is a pretty standard UICollectionView with custom cell inside ViewController.
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CollectionViewCell *cell = (CollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPath];
NSString *imageName = [NSString stringWithFormat:#"%#", [self.urlArray objectAtIndex:indexPath.row]];
[cell setImageName:imageName];
[cell updateCell];
return cell;
}
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return self.collectionView.frame.size;
}
EDIT:
I just removed the navigation bar and the first 2 images were loading behind the nav bar. They are loading in the size of the default prototype CollectionViewCell. Once I scroll away and back to it, it is the expected size.
When you don't put in constraints, IB automatically puts in constraints for you that give you an absolute position and size based on the frame in the storyboard. Chances are good that the constraints you're adding are not doing what you think they are.
Adding [cell layoutIfNeeded]; right before return cell in cellforitematindexpath fixed it
I'm building a status item kind of thing for a UICollectionView. My problem is when I want to add some text to the status area I can't get the thing to auto resize to the new text. I have auto layout on and I've tried all kinds of things found on stacky.
The one which I think is the closest to being correct is this:
-(UICollectionViewCell *) collectionView:(UICollectionView*)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
StatusItemModel *statusModel = [self.items objectAtIndex[indexPath indexPosition:0]];
StatusItemEventCell *statusCell = [collectionView dequeueResusableCellwithReuseIdentifier: #"EventStatusItem" forIndexPath:indexPath];
statusCell.statusTitleLabel.text = [statusModel.statusDetails valueForKey:#"title"];
statusCell.statusContentTextView.text = [statuaModel.statusDetails valueForKey:#"content"];
[statusCell layoutIfNeeded];
return statusCell;
}
// After which I believe we have to do some magic in this but what?
- (CGSize) collectionView:(UiCollectionView *) collectionView layout:(UICollectionViewLayout *) collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
// How do I get the size of the textview statusContentTextView.text?
// With that I'll be able to figure out what needs to be returned.
return CGSizeMake(299.f, 200.f);
}
The autolayout is setup with constraints for all elements in the cell. I've even played around with the intrinsic size and placeholders, however still now luck. Please can someone point me in the right direction.
So after going around in circles thinking there was a better way, no we need to know the size before we can set the size of the cell for the collection view. Pretty counter productive, because sometimes we don't know the size of it at run time. The way I solved this was to create a mock UITextView object and then called sizeThatFits.
So here is what I did with my code:
- (CGSize) collectionView:(UICollectionView *) collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
StatusItemModel *statusModel = [self.items objectAtIndex:[indexPath indexAtPosition:0]];
UITextView *temporaryTextView = [[UITextView alloc] init];
temporaryTextView.text = [statusModel.statusDetails valueForKey:#"content"];
CGSize textBoxSize = [temporaryTextView sizeThatFits:CGSizeMake(299.0f, MAXFLOAT)];
// Can now use the height of the text box to work out the size of the cell and
// the other components that make up the cell
return textBoxSize;
}
Inside the reusable view of my cell, I have a UIView.
Then, I have this method in the controller
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
// .. set value for CGFloat backgroundHeight
[cell addSubview:cell.backgroundView];
CGRect f = cell.backgroundView.frame;
f.size.height = backgroundHeight;
cell.backgroundView.frame = f;
}
But the UIView's height remains the same as specified in the Layout Rectangle.
What should I try next?
Your problem here lies in the fact that you are attempting to use the cell's backgroundView.
Firstly, you cannot add the cell's backgroundView as a subview. You simply assign a UIView to it with :
cell.backgroundView = yourView;
Secondly, if you read the docs, it clearly states :
Use this property to assign a custom background view to the cell. The background view is placed behind the content view and its frame is automatically adjusted so that it fills the bounds of the cell.
This means, no matter what frame you try to set for the backgroundView it will automatically adjust and fill the entire cell. Now, I haven't actually tried it, but you might be able to override this by subclassing. Though, i'll mention here, I am unsure.
Back to your problem, if you really want a UIView that you can control, you will need to create a UIView and then add it as a subview. Using the cell's backgroundView is not the solution.
It just seems like useless, what you'r approaching with the UICollectionViewCell's backgroundView .
By the Doc
backgroundView The view that provides the background appearance.
#property (nonatomic, retain) UIView *backgroundView; Discussion The
view (if any) in this property is positioned underneath all of the
other content and sized automatically to fill the entire bounds of the
collection view. The background view does not scroll with the
collection view’s other content. The collection view maintains a
strong reference to the background view object.
This property is nil by default, which displays the background color
of the collection view.
the backgroundView is just nothing but the cell, so what you'r upto do is doesn't effect . seems like directly changing the Cell's height.
the best solution is to just ignore the backgroundView property all
together. Instead, make the collection view’s background clear, and
implement your own View; just throw a view behind the
collection view.
Kindly check this blog, this would be helpful for you.
You can manage the Layout height With sizeForItemAtIndexPath
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
return CGSizeMake(view.frame.size.width, view.frame.size.height);
}
here you can manage spacing
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
return UIEdgeInsetsMake(5,5,0,5);
}
Controlling UICollectionViewCells is just the same as UITableViewCell. What you need to do is create a UICollectionViewCell subclass. These can seem confusing to start with but are really pretty simple to set up.
The first thing is to add any additional properties you might need like additional UIImageViews, UILabels etc. Now, we need to make sure all objects are instantiated but only once so this happens in
- (id)initWithFrame:(CGRect)frame
As well as instantiating an adding subviews to self.contentView set any global or default properties such as font, color etc. You can't set a frame relative to self.contentView yet though because self.contentView has zero size until layoutSubviews.
Now, create a method:
-(void)layoutSubviews
{
[super layout subviews];
...
[self.backgroundView setFrame: myFrame]; // in this case
}
The [super layoutSubviews] is important to set self.contentView's frame from the delegate cell layout methods. This method is called every time the cell comes into view or changes in any way (which is often). What you need to do now is set the various frames of things based on self.contentView.frame or self.frame. Also, you can set any conditional properties like hiding icons depending on a state etc.
To answer the question, you do not need to add self.backgroundView because it is already there. What you do need to do is set the frame in layoutSubviews as above but you need a UICollectionViewCell subclass in order to do that.
To use the custom cell you just need to include your new .h file and swap UICollectionViewCell for your new classname in the
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
method like:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPath];
cell.backgroundColor = [UIColor redColor];
return cell;
}
Do it like below, as you can not modify only height in frame you have to define new frame using CGRectMake function, after this you will also require to change the cell height also otherwise your view will be displayed in that much portion only.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
// .. set value for CGFloat backgroundHeight
[cell addSubview:cell.backgroundView];
CGRect frame = CGRectMake(cell.backgroundView.frame.origin.x, cell.backgroundView.frame.origin.y, cell.backgroundView.frame.size.width, backgroundHeight);
cell.backgroundView.frame = frame;
}
I have a UICollectionView in IB with a bunch of different prototype cells.
I have tried to give each cell its own size in IB so that when I instantiate it with dequeueReusable..., it will have that size. What I am finding however is that it doesn't matter what your settings in IB are for your cell, your collectionview uses its own cell size for all cells used inside it.
Is this a bug? If not what is the best way to set a custom size for all your cells?
I have also tried:
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
NSString* cellIdentifier = self.cellIdentifier;
ImageCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:self.cellIdentifier forIndexPath:indexPath];
return [cell sizeOfCell];
}
But for some reason the app crashes when dequeuing cells in this function.
I have subclassed all the prototype cells and have a function that has them return their own size. Is there any other way to instantiate a cell with the Identifier you gave it in IB?
The sizing of cells (for collection views and table views) in IB is just for layout purposes, it has no effect on the size at run time. You should implement the UICollectionViewDelegateFlowLayout method collectionView:layout:sizeForItemAtIndexPath: to return the correct size for each item.