Updating height of UITableViewCell - ios

I'm building a table that shows an entry for a user. One of the cells shows a set of "tags" downloaded from the server. I am currently building a set of UILabels and manually adding them to a view contained in the cell. While this works, the cell does not dynamically resize after adding the tags. The tags overlap the cell beneath it and I can't figure out how to manually update the height of the cell.
In cellForRowAtIndexPath:
JournalTagsCell *cell = [tableView dequeueReusableCellWithIdentifier:#"JournalTagsCell" forIndexPath:indexPath];
//Check if we have any tags to show
if(self.journalObject.journalEntryTags != nil){
cell.placeholderLabel.hidden = YES;
cell.tagsView = [self updateTagsView:cell.tagsView];
}
return cell;
The following is my method for actually creating each tag, laying them out and adding them to the view:
- (void)updateTagsView:(UIView*)viewToUpdate{
NSArray *items = self.journalObject.journalEntryTags;
//Clean up the view first
NSArray *viewsToRemove = [viewToUpdate subviews];
for (UIView *v in viewsToRemove) {
[v removeFromSuperview];
}
float x = 10;
float y = 10;
for (int i = 0; i < items.count; i++) {
CGRect textRect = [items[i] boundingRectWithSize:CGSizeMake(self.view.frame.size.width - 20, 1000)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:[UIFont tagCopy]}
context:nil];
CGSize size = textRect.size;
if (x+size.width > (self.view.frame.size.width-20)) {
y += size.height + 10;
x = 10;
}
UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(x, y, size.width, size.height)];
lbl.userInteractionEnabled = YES;
[lbl setText:[NSString stringWithFormat:#" %# ", items[i]]];
[lbl setFont:[UIFont tagCopy]];
[lbl sizeToFit];
[lbl setTextColor:[UIColor colorWithRed:0.145 green:0.392 blue:0.576 alpha:1.000]];
[lbl setBackgroundColor:[UIColor colorWithRed:0.804 green:0.871 blue:0.914 alpha:1.000]];
lbl.layer.borderWidth = 1;
lbl.layer.borderColor = [UIColor colorWithRed:0.145 green:0.392 blue:0.576 alpha:1.000].CGColor;
lbl.layer.cornerRadius = 5;
[lbl.layer setMasksToBounds:YES];
[viewToUpdate addSubview:lbl];
UITapGestureRecognizer *tapGesture =
[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(userClickedOnTag:)];
[lbl addGestureRecognizer:tapGesture];
x += size.width + 10;
if (x > (self.view.frame.size.width-20)) {
y += size.height + 10;
x = 10;
}
if (i == items.count-1) {
y+= size.height + 20;
}
}
[viewToUpdate setFrame:CGRectMake(0, 0, self.view.frame.size.width, y)];
}
However, I don't know how to manually update the height for this cell based on the size of this view. I don't want/need to manually calculate the height of every single cell, just this one which is why I'm not currently using heightForRowAtIndexPath but I don't know how to update the height of this one cell. Obviously I can calculate the height I need for this cell if necessary as I'm already setting up the view frame that holds the tags, but short of having to go through every single cell and manually calculate each ones height, I'm stumped.

I think you will have to use heightForRowAtIndexPath. I'm not sure you completely understand what's happening. Or maybe I have it wrong.. Either way, how I understand it: The total height of the "cell" will always be presented. You never set the "height" of the cell, it will automatically show the entire content. heightForRowAtIndexPath is not a way to tell the cell how tall it should be, but rather how much space the tableView should reserve for that particular cell. If you pass a height too short, it will still present the entire cell, but the next cell will start too soon. It also works the other way around, if you pass a bigger number than necessary, it will look like the cells are bigger, even though the cells aren't. It's just the tableView's representation.

If you are using iOS 8 you can use UITableViewAutomaticDimension.You can check out this example
self.tableView.rowHeight = UITableViewAutomaticDimension;
You can take a look also on this video : What's New in Table and Collection Views in the 2014 WWDC.

You can solve the issue with a variable for that row's height. In viewDidLoad() store default cell height to the variable. then while you calculate the height for the view store the view's height to the variable and to reload that cell use below method for the tableview by passing single cell's indexpath in array.
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
It will update cell at particular index by calling all lifecycle method of a cell.
Make sure you return default value for all other cell in heightForRowAtIndexPath() except the cell with tagsView with calculated height cell.
-(CGFloat)tableView:(UITableView *)myTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexpath.row == TAGSVIEW_ROWINDEX)
return calculated_height_value;
return default_row_height;
}
If the Cell with tagsView is not predefined with rowIndex, you can also store that rowIndex in one integer variable in cellForRowAtIndexPath().

Related

How to change the height of a custom cell individually within a UITableView?

I am trying to change the height of each cell individually based on how long my UILabel is, but it does not seem to change.
I have found the following question: How to change cell height dynamically in UITableView static cell
I tried the solution from that question:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
//return 110;
NewsFlashCustomCell *cell;
static NSString *CellIdentifier = #"CustomCell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
[cell layoutIfNeeded];
return cell.messageLabel.frame.origin.y + cell.messageLabel.frame.size.height;
}
However, that seems to do nothing.
Here's a screenshot:
I'd like the height of the cells containing "tidal wave" and "mashup" to be smaller than the last 2 cells since it has a longer message.
I cannot perform a indexPath.row check to change each cell individually because each message is pulled from a database, so the length of the message varies.
What can be done?
In case it helps, here's a screenshot of my constraints:
Thanks.
UPDATE:
I added the following code in viewDidLoad:
tableView.rowHeight = UITableViewAutomaticDimension;
This is what it looks like:
If you can't set try with other method reduce the label font size depend upon the cell height .
label.numberOfLines=0;
label.clipsToBounds=YES;
label.adjustsFontSizeToFitWidth=YES;
label.lineBreakMode = NSLineBreakByWordWrapping;
You can calculate height for label from its length of text and do it like following way:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return [self calculateHeightForCellAtIndexPath:indexPath];
}
#pragma mark - cell height handling
-(CGFloat)calculateHeightForCellAtIndexPath:(NSIndexPath *)indexP{
UILabel *lbl = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, self.tableView.frame.size.width-22, defaultHeightOfYourLabel)];
NSString *strLbl;
strLbl = [yourArray objectAtIndex:indexP.row];
lbl.text = strLbl;
if (IS_IPAD) {
[lbl setFont:fontRegular15];
}else{
[lbl setFont:fontRegular12];
}
UIFontDescriptor * fontDLBLTime = [lbl.font.fontDescriptor
fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold];
if (IS_IPAD) {
[lbl setFont:[UIFont fontWithDescriptor:fontDLBLTime size:15]];
}else{
[lbl setFont:[UIFont fontWithDescriptor:fontDLBLTime size:12]];
}
[lbl setLineBreakMode:NSLineBreakByClipping];
[lbl setNumberOfLines:0];
int noOfLines = [GlobalFunction lineCountForLabel:lbl];
CGFloat height = [GlobalFunction heightForWidth:lbl.frame.size.width usingFont:lbl.font forLabel:lbl];
if (noOfLines>1) {
return height + 33; //here 33 is sum of top and bottom space from view.
}
return 50; //default height of cell.
}
In your class implement following methods:
#define CGFLOAT_MAX_HEIGHT 2000.0
+ (int)lineCountForLabel:(UILabel *)label {
return ceil([self heightForWidth:label.frame.size.width usingFont:label.font forLabel:label] / label.font.lineHeight);
}
+(CGFloat)heightForWidth:(CGFloat)width usingFont:(UIFont *)font forLabel:(UILabel *)lbl{
NSStringDrawingContext *context = [[NSStringDrawingContext alloc] init];
CGSize labelSize = (CGSize){width, CGFLOAT_MAX_HEIGHT};
CGRect r = [lbl.text boundingRectWithSize:labelSize options:NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName: font} context:context];
return r.size.height;
}
You can customize lbl on basis of your font and label size.
Have your label constrain the bounds of your cell, like your already doing. Be sure to include padding around it so it looks nicer.
In your heightForRowAtIndexPath: calculate the height required for your label (it is important you have your label correctly configured for multiline and/or word wrapping)
Calculate the required height of label
CGRect frame = [label.text boundingRectWithSize:CGSizeMake(cell.width, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName: font} context:nil];
Return the calculated height plus padding
frame.size.height + TopPadding + Bottom Padding
This is a general approach to have a dynamically sizing cell.
Note: Sometimes this is off by a few points so you may need to add up 5 extra points in the cell height to get all the text fitting in the label.
Please write following line in viewDidLoad:
_tableView.rowHeight = UITableViewAutomaticDimension;
Please make sure of following points:
Dont create height constraint of label
Make width of that label equal to screen size or content view
Apply leading, trailing, top and bottom constraint
Dont write heightForRowAtIndexPath method
Please let me know the results
EDIT :
Write following line in your cellForRowAtIndexPath method
myLabel.preferredMaxLayoutWidth = cell.contentView.frame.size.width;

UITableView frame base on number of cell

As My title said that i want to set frame of UITableView base on number of cell. Cell's height of UITableView is dynamic it is not fix, for do this i apply my following logic
NOTE: I added UITableView on UIScrollView so it is easy to scroll/see whole table's content. And i know UITableView has own scrollView but in my project i need set height of UITableView base on number of cell (dynamic height of cell).
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self adjustHeightOfTableview]; // custom method for set height of tableView
}
- (void)adjustHeightOfTableview
{
CGRect frame = self.tblView.frame;
frame.size.height = tblHeight; // tblHeight is CGFloat, declare in .h file
self.tblView.frame = frame;
self.scrollView.contentSize = CGSizeMake(300, self.tblView.frame.origin.y + self.tblView.frame.size.height);
}
And get from
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"%d", indexPath.row);
NSString *AnswerHeight = [[self.listOfQuestions objectAtIndex:indexPath.row] objectForKey:#"Answer"]; // get
CGSize constraint = CGSizeMake(queWidth - (10.0f * 2), 20000.0f);
CGSize size = [AnswerHeight sizeWithFont:[UIFont fontWithName:#"OpenSans-Bold" size:12] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
CGFloat height = MAX(size.height, 52);
tblHeight = tblHeight + height + (10.0f * 2) - 1.5; // set tblHeight
return height + (10.0f * 2) - 1.5;
}
But problem is that heightForRowAtIndexPath call 2 times so i am not able to get proper tblHeight so my tableView's height not set properly.
Why my heightForRowAtIndexPath call 2 times or How can i solve my problem ?? or any other solution for set tableView's frame base on dynamic number of cell ??
Just for understanding
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 5;
}
why to not make empty footerView.
UIView *footerView=[[UIView alloc]initWithFrame:CGRectZero];
self.taskListTable.tableFooterView=footerView;
and then change the table frame where you thinks it will change:
like in viewWillApear or any of your action
self.taskListTable.frame= CGRectMake(0,0, 320, self.view.bounds.size.height);
Each row will call heightForRowAtIndexPath everytime it will appear in the visible rows, so the solution is quite simple.
Create an NSMutableArray, with number of rows as your table view, then everytime you calculate the height of a row, just change that array's index for that indexPath's row with the height of that row.
And whenever you want the height of the table, just sum all your array's rows, that should do it.
If you have any more clarifications, just ask (Y)
heightForRowAtIndexPath will call for each row which is going to visible.
You can calculate table height in viewDidAppear or ViewWillApearMethod.
for (int i =0 ; i < self.listOfQuestions.count; i++) {
NSString *AnswerHeight = [[self.listOfQuestions objectAtIndex:i] objectForKey:#"Answer"]; // get
CGSize constraint = CGSizeMake(queWidth - (10.0f * 2), 20000.0f);
CGSize size = [AnswerHeight sizeWithFont:[UIFont fontWithName:#"OpenSans-Bold" size:12] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
CGFloat height = MAX(size.height, 52);
tblHeight = tblHeight + height + (10.0f * 2) - 1.5; // set tblHeight
}
Check my updated answer

UICollectionView cell borders in a flow layout

I'm using UICollectionView to lay out a bunch of cells that are sectioned by first letter of their title. Each cell should have a very thin border around it, and the section headers should have borders above and below. Here's my current prototype:
I achieve the current appearance with the following rules:
Stroke the right and bottom edge of each cell.
Stroke the bottom edge of each section heading.
This is very close to what I want, but there are two defects:
If the line before a section heading isn't full, then the border along the top of the heading stops short of the right edge of the screen.
It's not visible in this screenshot, but if a line is full, the right border of the last cell in the line is still drawn, which looks a little odd against the edge of the screen.
My best idea to fix this is to somehow tell each cell if it's in the last row of a section or the last cell in a row; then the cell would turn off the offending borders, section headings would draw a top border as well as a bottom, and everything would be hunky-dory. I don't know how to achieve that, though.
Any thoughts on how to manage that, or another way to get the look I'm going for? I'm currently using a UICollectionViewFlowLayout.
I ended up subclassing UICollectionViewFlowLayout and applying several heuristics after the flow layout had calculated the attributes for each cell:
If center.y is equal to center.y of the last item in the section, the cell is in the last row of the section.
If CGRectGetMaxY(frame) is equal to CGRectGetMaxY(self.collectionView.bounds), then the cell is agains the right edge of the collection view.
I then stored the results of these calculations in a subclass of UICollectionViewLayoutAttributes, and wrote a UICollectionViewCell subclass whose -applyLayoutAttributes: method would adjust the borders its background view draws based on the additional properties.
I've put the whole mess into a fairly enormous gist so you can see exactly what I did. Happy hacking.
My best idea to fix this is to somehow tell each cell if it's in the last row of a section or the last cell in a row; then the cell would turn off the offending borders, section headings would draw a top border as well as a bottom, and everything would be hunky-dory. I don't know how to achieve that, though.
What you describe is more or less what I did in a similar scenario. I added a border property to my cell:
typedef NS_OPTIONS(NSInteger, TLGridBorder) {
TLGridBorderNone = 0,
TLGridBorderTop = 1 << 0,
TLGridBorderRight = 1 << 1,
TLGridBorderBottom = 1 << 2,
TLGridBorderLeft = 1 << 3,
TLGridBorderAll = TLGridBorderTop | TLGridBorderRight | TLGridBorderBottom | TLGridBorderLeft,
};
#interface TLGridCellView : UIView
#property (nonatomic) TLGridBorder border;
#end
Then I set the border in my view controller's cell configuration:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
TLGridCellView *cell = ...;
if (indexPath.item == self collectionView:collectionView numberOfItemsInSection:indexPath.section - 1) {
cell.border = TLGridBorderLeft;
} else {
cell.border = TLGridBorderLeft | TLGridBorderRight;
}
return cell;
}
I solve this problem in a simple way. I didn't add boarder to cell, instead I add a label with boarder into the cell. For the first column, the frame of the label is the same with the cell. For the other label, I set the x coordinate -0.5 to make their boarder overlap. Hope it helps.
Here is the code:
- (UICollectionViewCell *) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cell" forIndexPath:indexPath];
// Then use it
UILabel *label = nil;
if (cell.contentView.subviews.count > 0) {
label = cell.contentView.subviews[0];
} else {
label = [[UILabel alloc] init];
}
label.text = #"北京";
[label setTextAlignment:NSTextAlignmentCenter];
[label setFont:[UIFont systemFontOfSize:14]];
[label setCenter:cell.contentView.center];
CGRect frame = label.frame;
if (indexPath.row%4 == 0) {
frame.origin.x = 0;
} else {
frame.origin.x = -0.5;
}
frame.origin.y = 0;
frame.size.width = self.collectionView.frame.size.width / 4;
frame.size.height = self.collectionView.frame.size.height / 9;
[label setFrame:frame];
if (cell.contentView.subviews.count == 0) {
[[cell contentView] addSubview:label];
}
label.layer.borderWidth = 0.5;
label.layer.borderColor = [[UIColor lightGrayColor] CGColor];
cell.backgroundColor = [UIColor whiteColor];
return cell;
}

Trying to render a large columnar grid within a UITableView

I have created a sort of GRID using uitableView. For that I have taken various labels for showing grid type line I am using line image. So by considering Grid my tableview is having around 88 columns.
My issue is when I scroll it down, I am getting jerky effect. Its performance s very poor. I am creating around 108 label and each row is having 88 labels and 86 image views.
What step do I need to follow to improve scrolling performance???
I was using clearColor for label background. But later on I have removed those background colors.
Having read your problem again, I am thinking you are scrolling horizontally and vertically with an incredibly wide tableview. If this is the case then you need to switch to UIScrollView and attach each item to this view. UIScrollView will only load the views that are visible and provide the kind of scroll performance you desire.
It will be important to avoid this:
// Using ARC, therefore no release on the UILabel
- (void) viewDidLoad {
self.scrollView.backgroundColor = [UIColor whiteColor];
UIFont *font = [UIFont systemFontOfSize:24];
CGSize size = [#"10000:10000" sizeWithFont:font];
_scrollView.contentSize = CGSizeMake(10000 * size.width, 10000 * size.height);
for (int y = 0; y < 10000; y++) {
for (int x = 0; x < 10000; x++) {
NSLog(#"Loading: %d, %d", x, y);
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(x * size.width, y * size.height, size.width, size.height)];
label.font = font;
label.textAlignment = UITextAlignmentRight;
label.text = [NSString stringWithFormat:#"%d:%d", x, y];
[_scrollView addSubview:label];
}
}
}
While this will eventually load it will take ages while it loads up all of these labels and it will consume a ton of memory. You want to lazy load this view just like a TableView. I will write up an example this evening.
You need to give the table cell a reuse identifier. Otherwise, each time you create a completely new cell and consume more and more memory.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *const kReuseIdentifer = #"ReuseIdentifer";
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kReuseIdentifier] autorelease];
}
return cell;
}

Dynamic cell height iOS

I'm trying to dynamically recalculate the height of the cell that contain UITextView. The height of the cell depends on the height UITextView.
The source code below recalculate height for each cell in table. This is the some text from NSDictionary. After all heights has recalculated I reload my table view with new height that contain in array heightCells;
UITextView *tempTextView = [[UITextView alloc] init];
UIFont *font = [UIFont fontWithName:#"Helvetica" size:16.0];
[tempTextView setFrame:CGRectMake(10, 63, 300, 9999)];
[tempTextView setFont:font];
NSString *str = [d valueForKey:#"definition"]; // set long text
tempTextView.text = str;
CGRect frame = tempTextView.frame;
frame.size.height = tempTextView.contentSize.height + 20.0;
tempTextView.frame = frame;
[tempTextView sizeToFit];
int height = tempTextView.frame.size.height + 150.0; // set padding 150.0 px
[heightCells addObject:[NSNumber numberWithFloat:height]];
UITableView method:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return [[heightCells objectAtIndex:indexPath.row] floatValue];
}
On not retina display everything work good and the height recalculated correct, but on retina display I have problem with recalculation. I know that the retina and not retina have one 320x480 points view and it should not affect for recalculation. But in my case this is appear.
Please see screenshot below. As you can see on screenshot the bottom padding after share button a different for retina and not retina display. And I don't know which kind this problem.
Thanks for help!
It seems the tempTextView's frame differs when it is on the UITableViewCell. I described my working technique in this answer.

Resources