0

I have a particularly problem that i can't seem to solve i have looked at my code for a few hours and can't seem to fix the problem.

When i add messages to my tableview the messages overflows the wrapper within the UITableViewCell and sometimes the wrapper gets cutoff by the cell.

How do i make sure that my wrapper is always big enough to hold the txt (lblMessage) and the cell is big enough to hold the wrapper.

Thanks in advance !

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CGFloat height = [self heightForMessage:indexPath];

    if(height < 50)
    {
        NSLog(@"Normal : %d , row : %d", 100,indexPath.row);
        return 100;
    }    
    else
    {
        NSLog(@"Custom : %f , row : %d", 70 + height,indexPath.row);
        return 70 + height;
    }
}

-(CGFloat)heightForMessage:(NSIndexPath *)indexPath
{
    MessageObject * model = [self.chats objectAtIndex:indexPath.row];

    CGSize size = [model.message sizeWithFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:12] constrainedToSize:CGSizeMake(290, 100000) lineBreakMode:NSLineBreakByWordWrapping];

    return size.height;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString * NewsCellIdentifer = @"NewsCellIdentifier";
    UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:NewsCellIdentifer];

    UILabel * lblContact;
    UILabel * lblMessage;
    UILabel * lblDate;
    UIView * wrapper;

    if (cell == nil)
    {

        /* Top menu for login */

        wrapper = [[UIView alloc] initWithFrame:CGRectMake(10, 5, 300, 90)];
        [wrapper setBackgroundColor:[self.delegate.color colorWithAlphaComponent:0.6f]];
        [wrapper.layer setCornerRadius:6.0f];
        [wrapper.layer setShadowOffset:CGSizeMake(0, 2)];
        [wrapper.layer setShadowRadius:2.0f];
        [wrapper.layer setShadowOpacity:0.5f];
        [wrapper viewWithTag:19];
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:NewsCellIdentifer];

        /* Contact Name */

        lblContact = [[UILabel alloc] initWithFrame:CGRectMake(10, 0, 150, 30)];
        [lblContact setFont:[UIFont fontWithName:@"HelveticaNeue-Medium" size:13]];
        [lblContact setTag:13];
        [lblContact setTextColor:[self.delegate colorFromHexString:@"#fffbff"]];
        [lblContact setBackgroundColor:[UIColor clearColor]];

        /* Received Message */

        lblMessage = [[UILabel alloc] initWithFrame:CGRectMake(10, 20, 280, 50)];
        [lblMessage setNumberOfLines:0];
        [lblMessage setLineBreakMode:NSLineBreakByWordWrapping];
        [lblMessage setBackgroundColor:[UIColor clearColor]];
        [lblMessage setFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:12]];
        [lblMessage setTextColor:[self.delegate colorFromHexString:@"#fffbff"]];
        [lblMessage setTag:14];


        /* Date received */

        lblDate = [[UILabel alloc] initWithFrame:CGRectMake(10, 65, 65, 30)];
        [lblDate setText:@"4 hours ago"];
        [lblDate setFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:11]];
        [lblDate setTextColor:[self.delegate colorFromHexString:@"#fffbff"]];
        [lblDate setTag:15];

        /* Subview Logic */

        [wrapper addSubview:lblContact];
        [wrapper addSubview:lblMessage];
        [wrapper addSubview:lblDate];

        [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
        [cell.contentView addSubview:wrapper];
    }
    else
    {
        lblContact = (UILabel *)[cell.contentView viewWithTag:13];
        lblMessage = (UILabel *)[cell.contentView viewWithTag:14];
        wrapper = [cell.contentView viewWithTag:19];
    }

    MessageObject * model = [self.chats objectAtIndex:indexPath.row];

    CGFloat height = [self heightForMessage:indexPath];

    [lblMessage setFrame:CGRectMake(10, 25, 280, height)];
    [lblMessage setText:model.message];

    [lblContact setText:model.clientModel.firstName];

    if(height < 50)
    {
        NSLog(@"wrapper size : %d for row %d ", 90, indexPath.row);
        [wrapper setFrame:CGRectMake(10, 5, 300, 90)];
    }
    else
    {
        NSLog(@"wrapper size : %f for row %d ", height + 60, indexPath.row);

        [wrapper setFrame:CGRectMake(10, 5, 300, height + 60)];
        [lblDate setFrame:CGRectMake(10, height + 20, 65, 30)];
    }

    [cell.contentView setBackgroundColor:[UIColor greenColor]];
    [cell setBackgroundColor:[UIColor clearColor]];

    return cell;
 }
  • your approach is ok but you have to simulate the layouting of the cell to get the real size. it displays labels & wrapper and you have to individually get their sizes – Daij-Djan Sep 24 '13 at 15:27

6 Answers6

2

Consider significantly improve your code...Cause it's almost unreadable now
1) Get rid of hardcode e.g. 20, 280. Instead consider using constants (CONTACT_LABEL_TO_MESSAGE_LABEL_OFFSET, MESSAGE_LABEL_WIDTH)
2) Get rid of adding custom subviews to UITableViewCell and tagging them with hardcoded (see 1) numbers - instead subclass UITableViewCell, Prototype it in .xib or .storyboard, make convinient methods for your class - e.g setDate:, setMessage:, setContactName:. All frames should be calculated in your subclass -layoutSubviews method.

At first look:
1) you set width of 280 to lblMessage, when you constrained it's text to width of 290, it causes wrong height calculation

Petro Korienev
  • 4,007
  • 6
  • 34
  • 43
  • I will update my code when i got the first draft working, im doing everything programatically so no xib's storyboards or whatever – Jason Giorgio Meulenhoff Sep 24 '13 at 16:17
  • Well, good coding style from beginning of project saves time after... Nevertheless, check your frames step by step in debugger. It's something wrong with numbers – Petro Korienev Sep 24 '13 at 16:22
1

I recommend using a prototype cell and computing cell heights by literally configuring the prototype with the data for the given index path. Just dequeue a cell (yes, you can do this outside of cellForRowAtIndexPath) and hold onto it for doing height calculations. For dynamic height labels, you'd typically call [label sizeToFit] in the cell's configuration logic. There is a more detailed discussion of this technique in this question.

If you want to take a look at a 3rd-party library, my TLIndexPathTools library provides a base table view class TLTableViewController with built-in support for dynamic row height using prototypes:

  1. For static, non-data driven height, it will return the default height of the prototype. So, if your prototype comes from the Storyboard, it will honor the Storyboard custom cell heights.
  2. For dynamic, data-driven height, any custom cell that implements the TLDynamicHeightView protocol will automatically have the dynamic height calculated.
Community
  • 1
  • 1
Timothy Moose
  • 9,895
  • 3
  • 33
  • 44
1

One of the errors is when you are creating the wrapper view for a new cell the line:

[wrapper viewWithTag:19];

should be:

[wrapper setTag:19];

This probably causes wrapper to be nil when you attempt to resize it on a reused cell.

Ian L
  • 5,553
  • 1
  • 22
  • 37
  • Now i only have a little problem with the lblDate dissapearing sometimes .. any idea's? – Jason Giorgio Meulenhoff Sep 24 '13 at 16:16
  • It's probably due to reusing cells. If the height was quite big, then the cell gets reused for a smaller message, the lblDate label's frame is not being reset to it's original place. – Ian L Sep 25 '13 at 08:13
1

I'll tell you the approach i used to tackle this issue.

  • Used an array to store heights (using sizeWithFont). It is deprecated now, so you should try using sizeWithAttributes instead.
  • Log the height from the array, and the height of the actual label, and see if they match.
  • Make sure the UIFont used is the same in sizeWithFont, and yourLabel.font.
  • Check to see if you have taken any kind of padding in the cells, like leaving pixels on the top of the cell.

These are the general tips.

n00bProgrammer
  • 4,261
  • 3
  • 32
  • 60
0

When you create a cell in tableView: cellForRowAtIndexPath: you can store it into a NSMutableArray
Then when tableView: heightForRowAtIndexPath: simply retrieve the cell and specify it height.

It's how I do it most of the time but if you use a lot of cell it might use to much memory. Or maybe you can only store the size of the cell in the NSMutableArray.


EDIT:

I'm not sure I understand you correctly.
You want your cell to be able to contain you wrapper?

cell.contentView.frame = CGRectMake(0, 0, cell.contentView.frame.size.width, wrapper.frame.origin.y + wrapper.frame.size.height);

Store the cell in an NSMutableArray in tableView: cellForRowAtIndexPath:, retrieve it based on it index in tableView: heightForRowAtIndexPath:

Martin Magakian
  • 3,746
  • 5
  • 37
  • 53
  • 1
    that works well for few cells -- it is hawkish though. better would be to have a prototype cell that you for measurements again and again and then init/reuse the right one in cellForRow – Daij-Djan Sep 24 '13 at 15:26
0

To calculate your Label height you should consider different things, You have to calculate size by keeping text length and its font in mind and the bounds of frame you are adding in. Below is example how to calculate size for dynamic text.

CGSize boundingSize = CGSizeMake(frame.size.width, CGFLOAT_MAX);
    CGSize requiredSize = [text sizeWithFont:yourFont constrainedToSize:boundingSize lineBreakMode:NSLineBreakByWordWrapping];
Adnan Aftab
  • 14,377
  • 4
  • 45
  • 54