UICollectionView Custom Layout - Cells are not properly arranged - iphone

In my iPhone app,I want to use custom lay out in UICollectionview.There will different heights for Collection view cells.I want to set a lay out like that.
I tried many things and at last,made that height adjustment.(I am getting height for each object from server)
But the layout is not in a proper way.There are lots of spaces in between the items.
How can I achieve a proper layout.
Now I am getting a view like this
What I want is -
Here is my code -
-(CGFloat)collectionView:(UICollectionView*)collectionView layout:(UICollectionViewLayout*)collectionViewLayout heightForItemAtIndexPath:(NSIndexPath*)indexPath
{
return 60;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return array.count; // getting from server
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
CollectionCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"CollectionCell" forIndexPath:indexPath];
cell.imageBG.image = [imagearray objectAtIndex:indexPath.item];
return cell;
}
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
CGSize retval;
// I'm taking height of each images into HeightArray
retval = CGSizeMake(100, [[HeightArray objectAtIndex:indexPath.item]floatValue ]+50);
return retval;
}

From your screenshots it seems you are trying to use standard UICollectionViewFlowLayout (or its subclass). Unfortunately its limitation is that each row will always occupy the same height - it will be the same as in highest cell in that row, so you it is not suitable for your task and you have to implement your own layout.
Check UICollectionViewWaterfallLayout class which, although has its limitations (supports only 1 section, no decoration views) provides layout you need and at least can be a good starting point if you won't be able to use it as it is now.

Related

App runs slower when collection subview has a size increase?

I am utilizing a UICollectionView for my universal app. Inside of each collection view cell I have a custom view, which ultimately hold's an image. Each cell is representing a playing card, so when tapped it flip's from front to back.The issue that I am having is when I scale up the size of that subview. If I have it smaller within the cell then everything run's fine. There are no slow down's of any kind. If I then scale up that subview to be larger then there are serious slowdown's. Not only while I scroll through the collection view up to down, but also when I tap the individual cell's. The lag happen's when a row of cell's comes from off screen. There is also lag when I tap a 'card' and it flip's from front to back.
This problem appear's to only exist on the iPad version of the app. When I run it on my iPhone there are no problem's at all in regards to lag.
Is there any reason as to why this would be happening?
Edit to add code:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"Card" forIndexPath:indexPath];
Card *card = [self.game getCardFromPlayAtIndex:indexPath.item];
[self updateCell:cell usingCard:card withAnimation:NO];
return cell;
}
Abstract method that is used above:
- (void)updateCell:(UICollectionViewCell *)cell usingCard:(Card *)card withAnimation:(BOOL)isAnimated
{
// Using introspection to make sure that our cell is a PlayingCardCollectionViewCell
if ([cell isKindOfClass:[PlayingCardCollectionViewCell class]]) {
// If it is we an take that cell and cast it to a PCCVC and then pull out it's
// outlet for the PlayingCardView
PlayingCardView *playingCardView = ((PlayingCardCollectionViewCell *)cell).playingCardView;
if ([card isKindOfClass:[PlayingCard class]]) {
PlayingCard *playingCard = (PlayingCard *)card;
playingCardView.rank = playingCard.rank;
playingCardView.suit = playingCard.suit;
playingCardView.faceUp = playingCard.isFaceUp;
playingCardView.alpha = playingCard.isUnplayable ? 0.3 : 1.0;
}
}
}

Expanded UITableViewCell according to UILabel size

I have a UITableView with 4 UILabel's: Title, Body, Author and Date, he looks like this:
What I want to accomplish is, when user click on the cell itself, another label should be added to the cell, the "Body" label and the cell should expand according to this label size.
Something like this:
How can I do that? I've searched stackoverflow, tried some code pieces, but still didn't found the right solution.
Thanks!
Edit 1: 14.11.12 at 14:52
I managed to change the size of the UILabel with the current text:
- (CGRect )resizeLabelByFontSize:(UILabel *)customCellLabel withMaxHeightSize:(CGFloat )maxHeight
{
CGSize maximumLabelSize = CGSizeMake(239, maxHeight);
CGSize expectedLabelSize = [customCellLabel.text sizeWithFont:customCellLabel.font constrainedToSize:maximumLabelSize lineBreakMode:customCellLabel.lineBreakMode];
//adjust the label the the new height.
CGRect newFrame = customCellLabel.frame;
newFrame.size.height = expectedLabelSize.height;
return newFrame;
}
But how can I change the size of the cell according to the size of the new UILabel?
By seeing Images in Question
Here is the method which just create the Dynamic FRAME for UILabel have a look at this
By getting the Height and Width for UIlabel you can calculate the Whole height and could set the Row Height of UITableView.
- (void)setLabeltextWithVerticalAlignTop:(NSString *)theText
{
CGSize labelSize;
// here labelSize is hard-wired but could use constants to populate the size
labelSize = CGSizeMake(210, 129);//this is just for example
//now create the Size from textString SO that We could assign this size to the Label.
CGSize theStringSize = [theText sizeWithFont:lblTitle.font constrainedToSize:labelSize lineBreakMode:lblTitle.lineBreakMode];
lblTitle.frame = CGRectMake(lblTitle.frame.origin.x, lblTitle.frame.origin.y, theStringSize.width, theStringSize.height);
lblTitle.text = theText;
}
Call Above Method For setting the height and Width of description Label you need to pass the text to be shown on that description label.
As you gets the height for that Label, Now On the Basis of this You can Adjust the heigh of Row of TableView.
EDIT:Above Code Just Create the Dynamic Frame For The UILabel
You should take a view of this this is what you looking for....!!!.here you would find a sample code too.
EDIT:As you edited your Question see ,it just the logic which you need to convert it into runnable code here it is.
Use Below Method in Your Code called for each row, and make some calculation inside it.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat rowHeight=0.0;
//here it seems cell have 4 subview added on it.
//so if you could calculate the totla hieht of them.
//so what you really need to do.you just use hieght calculative Method for getting hieght of each of three UILabel
//you need to modify `setLabeltextWithVerticalAlignTop` method .
rowHeight= [self setLabeltextWithVerticalAlignTop:#"pass the correspondingText"];// suppose it returns some hieght for FisrtLabel.
//suppoose here you get the 20.0 height here
rowHeight= rowHeight+[self setLabeltextWithVerticalAlignTop:#"pass the correspondingText"];
// suppose it returns some hieght for secondUIlabel.
//suppoose here you get the 40.0 height here
rowHeight= rowHeight+ [self setLabeltextWithVerticalAlignTop:#"pass the correspondingText"];
// suppose it returns some hieght for ThirdUIlabel.
// suppoose here you get the 15.0 height here
//here you have totla height you just need to add some gapping floating value for all of three UIlabel.so that the could not overlap like as.
rowHeight= rowHeight+20.0;
//now you can return that total height
return rowHeight;
}
Note:This is just logic you need to convert it into runnable code.i am sure this can help.
I hope it may help you.
Implement the following methods
– (void) tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
// cast cell, add label, expand labels etc
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [indexPath isEqualTo:[tableView indexPathForSelectedRow]] ? /* expanded height */ : 80 /* normal height */;
}
If you want the row to stay selected even after another row is selected then add a custom BOOL property to your custom cell, e.g. expanded, and use that to determine the height.
You can use tableView:didSelectRowAtIndexPath
In that method you can then create your code to unhide the body label, adjust the relative positions of everything else. Calculate the new height of the row and then call the Table View's reloadRowsAtIndexPath: withRowAnimation: method.
Sorry if there's not a lot of detail in that, but hopefully that should get you on the right track.
Ok, firstly... To expand you need something like this:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
Now here is the catch:
You should calculate the size of the UITableViewCell (expanded and non-expanded)
Doing so when you are actually scrolling might be expensive and will give you a bad experience
My advice:
Calculate both sides, before you have actually conclude to build the UITableView, since you want to have dynamic sizes. If you don't and all cells will have the same size expanded, you can use what lammmert said.
NSIndexPath *selectedRow;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
selectedRow = indexPath;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
if(indexPath == selectedRow){
//return your custom value
}
return 100;
}
I think it will look something like that
So, in order to do this, using expended UITableViewCell, i've created 2 different custom cells, at start the table is showing the first cell, when I click on the cell, the table is showing the second one. Its that easy - yeah!
So I have the UIViewController with the UITableView that implements the table delegate methods:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if([self.selectedCellIndexPath isEqual:indexPath])
{
return [self expandedCellHeight:indexPath];
}
else
{
return kRegularCellHeight;
}
}
-(CGFloat)expandedCellHeight:(NSIndexPath *)indexPath
{
CGSize maxSize = CGSizeMake(303, 200);
NSString* bodyText = [[self.data objectAtIndex:indexPath.row] objectForKey:kForumMessagesBody];
CGSize fitSize = [bodyText sizeWithFont:[UIFont systemFontOfSize:13] constrainedToSize:maxSize lineBreakMode:UILineBreakModeWordWrap];
CGFloat height = 384 - 69 + fitSize.height;
NSLog(#"expandedHeight: %f",height);
return height;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Answer cell
if ([self.selectedCellIndexPath isEqual:indexPath])
{
cell = [tableView dequeueReusableCellWithIdentifier:[ForumCell expandedAnswerReuseIdentifier]];
if (cell == nil)
{
cell = [ForumCell expandedAnswerCell];
}
self.expandedCell = cell;
}
else
{
cell = [tableView dequeueReusableCellWithIdentifier:[ForumCell reqularAnswerReuseIdentifier]];
if (cell == nil)
{
cell = [ForumCell regularAnswerCell];
}
}
cell.labelMedia.text = [self.data objectAtIndex:indexPath.row];
return cell;
}
I also have custom cell, the class called ForumCell.h and ForumCell.m and it has 2 different XIB files: ForumRegularAnswerCell.xib and ForumExpandedAnswerCell.xib, I have the following code inside ForumCell.h:
+ (NSString*)reqularAnswerReuseIdentifier
{
return #"RegularAnswerCellReuseIdentifier";
}
+ (NSString*)expandedAnswerReuseIdentifier
{
return #"ExpandedAnswerCellReuseIdentifier";
}
+ (ForumCell*)regularAnswerCell
{
NSArray* objs = [[NSBundle mainBundle] loadNibNamed:#"ForumRegularAnswerCell" owner:self options:nil];
ForumCell* result = [objs objectAtIndex:0];
return result;
}
+ (ForumCell*)expandedAnswerCell
{
NSArray* objs = [[NSBundle mainBundle] loadNibNamed:#"ForumExpandedAnswerCell" owner:self options:nil];
ForumCell* result = [objs objectAtIndex:0];
return result;
}
- (id)initWithCoder:(NSCoder *)decoder
{
self = [super initWithCoder:decoder];
if (self)
{
_originalCellHeight = self.frame.size.height;
_originalLblBodyHeight = self.lblBody.frame.size.height;
}
return self;
}
You can also use more than 2 xibs if you'd like its up to you. but this is the basics.
Enjoy!

UITableView showing more rows than specified in numberOfRowsInSection:

I want my tableView to show 6 rows with text in it, in this case "Example." As far as I can tell, I have my numberOfSectionsInTableView: and numberOfRowsInSection: set properly. See example code below:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
// Return the number of rows in the section.
return 6;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = #"Example";
return cell;
}
The problem is when you see the image below showing lines for rows that shouldn't/don't exist.
How do I get rid of the lines showing past row 6?
The generally accepted way of doing this is to add a footer view with a frame size of CGRectZero, as such:
[tableView setTableFooterView:[[UIView alloc] initWithFrame:CGRectZero]]
What this does is tell the table that there is a footer, and so it stops displaying separator lines. However, since the footer has a CGRectZero as its frame, nothing gets displayed, and so the visual effect is that the separators simply stop.
Swift Version
The easiest method is to set the tableFooterView property:
override func viewDidLoad() {
super.viewDidLoad()
// This will remove extra separators from tableview
self.tableView.tableFooterView = UIView(frame: CGRect.zero)
}
This is Because of Your Table-view Height. Weather you have Write
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
// Return the number of rows in the section.
return 6;
}
But its show rows According to Table-view Size. If you Dont want to show This extra Lines then Make UITableView Style Plain To Grouped.
Short and simple answer..
self.tableView.tableFooterView = [UIView new];
You could do something along the lines of:
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:7 inSection:0];
[self.mytableView cellForRowAtIndexPath:indexPath].hidden = YES;
Im sure there are some better ways but this is the first thing that came to mind.
If you're referring to the light gray lines that appear below the last row, that's simply the default way a UITableView draws the row separator.
You could try changing the Separator style in Interface Builder (see the images below) to see if one of those might be more to your liking.
You didn't say what you do want to see past the last row. If you just want to see the window background, then just embed your table view in a UIView that's just tall enough to show the number of rows you want to see. If you want to see more rows without scrolling, then you would have to adjust the size of that containing view based on the number of rows.
To programmatically remove it, use this:
[yourTableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
It's a lot easier to:
return numberOfSections + 1
return 0 rows in the final section
This keeps it simple!

Adjust UITableViewCell Height and Indentation?

Two UITableViewCell related questions:
In my custom UITableViewCell I loop through an array (of which I do not know how many objects it holds) and add a UILabel displaying some text for each object in that array.
This means I have to adjust the height of the cell so that these labels fit in. How can I do this?
When going into edit mode, I have the cells indent, however I do not want this. I have tried the following:
cell.shouldIndentWhileEditing = NO;
and
-(BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}
Both sadly failed, I have I no idea why. How could I possible remedy this?
Any help is much appreciated with either of these issues, thanks.
You can specify the height for every row with the delegate method [UITableViewDelegate tableView:heightForRowAtIndexPath:].
Just change what the method returns and the reload your table.
This method actually has nothing to do with the indendation of cell content:
Asks the delegate whether the background of the specified row should be indented while the table view is in editing mode.
You can try to set indentationWidth but I never managed to make it work.
Fortunately, it's easy to change everything you want in [UITableView layoutSubviews] method.
Example:
- (void)layoutSubviews {
[super layoutSubviews];
self.contentView.frame = self.bounds;
}
You may also need to set
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath;
{
return UITableViewCellEditingStyleNone;
}
For the dynamic height part there are plenty of other answers and a quick google found a tutorial here

Change height of TTTableViewCell dynamically

I am using TTTableViewController class. I have added the UIView of height 100 to the Table cell initially. The height of UIView changes dynamically and I have to change the height of cell also. But height of the cell doesnt chnages. please remember I am ?Using Three 20. Thanks in advance for valuable response.
Have you Enabled this while creating row Dynamically??
BOOL variableHeightRows = YES
You can refer this link, hope it may help you. :))
Or try this :-
-(CGFloat)tableView :(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// return (the value for the row)
}
but for this you must implement its Delegate i.e. UITableViewDataSource.
use the below delegate method to adjust table row height.
-(CGFloat)tableView :(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 50; //the required table row height
}
For TTTableViewCell
do it in didLoad or init method
self.variableHeightRows = YES;
and use the below function.
+ (CGFloat)tableView:(UITableView*)tableView rowHeightForObject:(id)object;
{
return 80; //value can be changed
}