Self Sizing Collection View Cell in iOS - ios

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.

Related

IOS : UICollectionView inside UIScrollView index not incremented

Hello people of stackoverflow,
Currently working on an App where I have a user profile that looks like this
http://i.imgur.com/H1N0ouX.jpg (sorry I dont have enough reputation to post the image)
Header View is a picture of the user.
Tab View is 2 tabs, one with a collectionview, the other with a tableview.
All those views are nested inside a scrollView
My problem is that when I scroll that view (collection view height set programmaticaly depending on number of elements), the collection view's index does not increment. Only the 6 first items are loaded in in the visible cells index of the collection view.
I need to segue from the collectionview's items to the item's detail view.
How can I use the scrollView delegate to know where my collection view is at.
I've looked at those threads :
iOS 7 Collection View inside Scroll View
How to implement non-scrollable UICollectionView inside UIScrollView?
But none gave me the answer I was looking for.
I also tried looking at scrollViewDidEndDecelerating and setting my collection view scroll offset according to the scroll view offset, but I could not make it work.
Any advices?
Thanks
EDIT :
Hello and thanks for your answer,
To answer your question, I can scroll my collectionview, since it is inside a scroll view, I set my collectionview height during viewdidload (based on number of elements)
my problem is, I can click on the first 6 items , and display details (via a segue) but after that, selection is not recognized as the index is not refreshed for my collection view. I can scroll all the way to the bottom of my collection view (I scroll via the scrollView).
My collectionView has user interaction enabled checked, as I said, I can select the first 6 items, and then selection is not recognized.
I understand the UICollectionViewFlowLayout issue, but where do I write this line ? in the viewDidLoad? or in delegate funcs of CollectionView ?
If you need more information, I can copy some code or show you the layout of the view in storyboard
Thanks in advance.
EDIT 2 :
images , storyboard : layout of my view
http://i.gyazo.com/c491786507db2effc702d910020515a2.png
code, here are the datasource funcs of the collectionView
http://gyazo.com/3730caeb2b2a40fef9efec559171744f
delegate func of the collection view
http://i.gyazo.com/81fc9443b4367ecbd304e608ab0cc864.png
EDIT3:
Okay so basically this is what I want to reproduce,
tab view in the middle with multiples tabs with collection view, all this scrollable.
What I cant understand, is if I set my topView as the header of the CollectionView, I can't switch tabs like I want since its inside the header.
ViewController with TalbeView and CollectionView tabs like Google+ in IOS
Hi First of all I need more detail about your question but I am assuming that you want your UICollectionView scrollable and actually its not scrolling.
So could you check following points :-
UserEnableInteraction for collection view should be enabled.
And also check what is the value you gave for UICollectionViewFlowLayout because just consider if You have total 5 elements to show and your UICollectionViewFlowLayout size is like it can show all five items in visible cell index then it doesn't scroll so you need to increase the size of UICollectionViewFlowLayout to make it scroll.
e.g
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setItemSize:CGSizeMake(148, 148)];
And if you want then you can get the help from UIScrollViewDelegate method like
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if (!decelerate)
{
// do your job
}
}
set delegates and Datasource in .h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController<UITableViewDataSource , UITableViewDelegate ,
UIScrollViewDelegate , UICollectionViewDataSource , UICollectionViewDelegateFlowLayout>
You can do that .m
in ViewDidLoad:-
UICollectionViewFlowLayout *layout=[[UICollectionViewFlowLayout alloc] init];
UICollectionView *collectionView =[[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:layout ];
collectionView.frame = CGRectMake(5, 61, 365, 678);
collectionView.delegate = self;
collectionView.dataSource = self;
[collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:#"cellIdentifier"];
[self.view addSubview:collectionView];
And then Call her delegates method :-
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return [arrImages count];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:#"cellIdentifier" forIndexPath:indexPath];
[self createCell:cell forTableview:collectionView];
[self fillCell:cell cellForRowAtIndex:indexPath];
return cell;
}
//- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
//{
// return CGSizeMake(50, 50);
//}

Constraints not updated in UICollectionViewCell subclass on iOS7

I have a collection view based layout with different cell sizes depending on the content. A regular cell is 200x200 px, but if there is no content I display a cell with the same size as the collection view itself.
I use
- (CGSize)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath
to calculate the correct size.
How ever, my subviews to the cell does not update its constraints. The cell is really simple, just a UILabel that should be centered within the cell superview. I have a horizontal and a vertical center constraint (also tried to pin each edge to the superview). The result is that the subviews gets the same size and position as entered in Interface Builder (Storyboard).
I've set background colors for both the cell and the label and can see that the cell gets the correct size, but the label does not.
The problem only exists in iOS7 and works as it should in iOS8.
Please help!
Thank you Stackover flow related questions pane! Found this thread and it solved my problem.
Auto Layout in UICollectionViewCell not working
I put this in my UICollectionViewCell subclass:
- (void)setBounds:(CGRect)bounds {
[super setBounds:bounds];
self.contentView.frame = bounds;
}
For me when this issues is coming on IOS9 with swift 2. I just called awakeFromNib() and set autoresizingMask in UICollectionViewCell.
My UICollectionViewCell looks like this-:
override func awakeFromNib() {
self.contentView.autoresizingMask = [UIViewAutoresizing.FlexibleWidth , UIViewAutoresizing.FlexibleHeight]
}

How to remove UITableView's NSAutoresizingMaskLayoutLayoutConstraint?

I designed a custom UITableViewCell by adding some subviews in the cell's contentView, I also added some auto layout constraints between the contentView and the subviews.
But when I debug the app, Xcode tells me that there is a constraint conflict. In the list of constraint, there is one NSAutoresizingMaskLayoutLayoutConstraint that limits the cell height to be 43, so Xcode break the constraint of my subview height and 'compress' it.
I have tried:
In Interface builder, uncheck the "autoreize subviews" checkbox. Doesn't work.
In code, cell.contentView.translatesAutoResizingMaskIntoConstraints = NO. This causes the app to crash with an exception: "Auto Layout still required after executing -layoutSubviews". I have tried every proposed solution in this question: "Auto Layout still required after executing -layoutSubviews" with UITableViewCell subclass None of them work for me.
So I guess I can only let the cell do its autoresizing thing, and remove the auto resizing constraint in code. How should I do it without breaking things?
EDIT:
Or, from another perspective, how I can make the tableViewCell height flexible (changes with subview height and constraints)? In IB, I have to set its height, right?
You DON'T need to set cell.contentView.translatesAutoResizingMaskIntoConstraints = NO
In order to get the flexible height in UITableViewCells in autolayout you need to manually compute for the height in - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
Yes this tedious, but there is no other way. To calculate the height automatically you need to fulfill two conditions in your UITableViewCell:
You must make sure all your subviews have
translatesAutoResizingMaskIntoConstraints=NO
Your cell subview's constraints must be pushing against the top and bottom edges of the UITableViewCell.
Then in your - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath, You need to recreate that cell for the specific indexPath and compute the height manually for that cell.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath
*)indexPath
{
//Configure cell subviews and constraints
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
[self configureCell:cell forIndexPath:indexPath];
//Trigger a layout pass on the cell so that it will resolve all the constraints.
[cell setNeedsLayout];
[cell layoutIfNeeded];
//Compute the correct size of the cell and get the height. This is where the magic happens.
CGFloat height = [cartItemCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
height += 1.0f
return height;
}
Take note that systemLayoutSizeFittingSize is wonky with UITextViews. In that case you have to compute the height of the entire cell manually using another way. There are also some performance optimizations you can do by caching the height per indexPath.
This blog post has a a more detailed description on what to do but the gist is essentially what I mentioned above. : http://johnszumski.com/blog/auto-layout-for-table-view-cells-with-dynamic-heights
I have created some sample code for dynamic tableview cell height with auto layout. You can download the project using following link.
DynamicTableViewCellHeight
I hope this will help you.

how to set dynamic uicollectionviewcell size from its content - programmatically

I need to have just simple UICollectionViewCell style with cells on top of eachoher. Like tableview. But I need Dynamic height dependent of the content, size the content is comments it can vary.
I got
viewDidLoad:
[self.commentsCollectionView registerClass:[GWCommentsCollectionViewCell class] forCellWithReuseIdentifier:#"commentCell"];
in .h I got:
and I #import my custom UICollectionViewCell that sets all constraints with programmatic autolayout.
I instantiate the UICollectionView with:
UICollectionViewFlowLayout *collViewLayout = [[UICollectionViewFlowLayout alloc]init];
self.commentsCollectionView = [[UICollectionView alloc]initWithFrame:CGRectZero collectionViewLayout:collViewLayout];
I use autolatyout to get the UICollectionView be where I want (thats why CGRectZero).
And finally I was hoping to do this:
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
GWCommentsCollectionViewCell *cell = (GWCommentsCollectionViewCell*)[collectionView cellForItemAtIndexPath:indexPath];
return cell.singleCommentContainerview.bounds.size;
}
singleCommentContainerview is a direct subview of the contentView and withing the singleCommentContainerview I have UILabels, UIImageViews etc, all set witih autolayoutcode.
But I just get cgsize value of (0,0)
How can I fix this to get the proper size I need for each cell?
From what I have read UICollectionView needs the sizes worked out before laying out the cell. So the above method of yours that cell hasn't yet been drawn so it has no size. Also it could be an issue or combined with the issue that the cell is cached/pooled with the same identifier #"commentCell", I tag unique cells with a new identifier and class normally.
My thoughts are to catch the cell before it is drawn, push the size into a dictionary for use later, using:
- (void)collectionView:(UICollectionView *)collectionView
willDisplayCell:(UICollectionViewCell *)cell
forItemAtIndexPath:(NSIndexPath *)indexPath{
GWCommentsCollectionViewCell *cell = (GWCommentsCollectionViewCell*)[collectionView cellForItemAtIndexPath:indexPath];
// Need to add it to the view maybe in order for it the autolayout to happen
[offScreenView addSubView:cell];
[cell setNeedsLayout];
CGSize *cellSize=cell.singleCommentContainerview.bounds.size
NSString *key=[NSString stringWithFormat:#"%li,%li",indexPath.section,indexPath.row];
// cellAtIndexPath is a NSMutableDictionary initialised and allocated elsewhere
[cellAtIndexPath setObject:[NSValue valueWithCGSize:cellSize] forKey:key];
}
Then when you need it use that dictionary based off the key to get the size.
Its not a really super pretty way as its dependent on the views being drawn, autolayout doing its thing before you get the size. And if you are loading images even more it could throw up issues.
Maybe a better way would be to preprogram the sizes. If you have data on the images sizes that may help. Check this article for a really good tutorial (yah programatically no IB):
https://bradbambara.wordpress.com/2014/05/24/getting-started-with-custom-uicollectionview-layouts/
Add
class func size(data: WhateverYourData) -> CGSize { /* calculate size here and retrun it */}
to your custom cell and instead of doing
return cell.singleCommentContainerview.bounds.size
it should be
return GWCommentsCollectionViewCell.size(data)

How to set size of prototype UICollectionViewCell

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.

Resources