Simon say’s Project-D UITableViewCells autosize based on text height in MonoTouch. Here’s how!

Written by Simon Guindon. Posted in Coding, Diggify, Mobile, MonoTouch, Project-D

Project-DWhile building the main story view in Project-D which uses a UITableView I needed to have each cell dynamically snap in their height based on the height of the content, which in this case is text.

To do this you must implement UITableViewDelegate along with UITableViewDataSource.

When implementing UITableViewDelegate the key override is GetHeightForRow(). This method lets us return what the height is for a particular row.

I’m doing 2 key things for performance reasons in GetHeightForRow() which are:

  1. Caching a font instance which represents the font I am using on the UITableViewCell
  2. Caching the cell height in my model, so that I only ever do this calculation once for a particular UITableViewCell

Basically what I am doing here is giving the measuring API a constraint width wise, and it will give me back a size of how high the text with this particular font consumes:

[code]
public class StoryViewTableController : UITableViewDelegate
{
private static UIFont font = null;
private StoryViewController viewController = null;

public StoryViewTableController(StoryViewController viewController)
{
this.viewController = viewController;
if (font == null)
font = UIFont.FromName("Helvetica", 14.0f);
}

public override float GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
{
Story story = viewController.DataSource.Stories.Stories[indexPath.Row];

if (story.CellSize.Height == 0)
{
story.Title = story.Title.Trim();
story.Description = story.Description.Trim();

story.TitleSize = tableView.StringSize(story.Title, font, new SizeF(237.0f, 1000.0f), UILineBreakMode.WordWrap);
story.DescriptionSize = tableView.StringSize(story.Description, font, new SizeF(237.0f, 1000.0f), UILineBreakMode.WordWrap);

SizeF sizeTotal = new SizeF(237.0f, story.TitleSize.Height + story.DescriptionSize.Height + 20);
story.CellSize = sizeTotal;

if (story.CellSize.Height < 108)
story.CellSize = new SizeF(story.CellSize.Width, 108);
}

return story.CellSize.Height;
}
}
[/code]

In my UITableViewDataSource I am doing the same technique I showed in the tutorial
Let’s Build A Custom UITableViewCell and applying the new sizes that I store in the model to the cell itself.

[code]
public override UITableViewCell GetCell(UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
UITableViewCell cell = null;
StoryCellController controller = null;

cell = tableView.DequeueReusableCell("StoryCell");
if (cell == null)
{
controller = new StoryCellController();
NSBundle.MainBundle.LoadNib("StoryCellController", controller, null);
cell = controller.Cell;

cell.Tag = Environment.TickCount;
cellControllers.Add(cell.Tag, controller);
}
else
{
controller = cellControllers[cell.Tag];
}

Story story = stories.Stories[indexPath.Row];
controller.Title = story.Title.Trim();
controller.Description = story.Description.Trim();

// Sizing the cell.
RectangleF rectCell = cell.Frame;
rectCell.Height = story.CellSize.Height;
cell.Frame = rectCell;

// Sizing the background image.
RectangleF rectImageBackground = controller.ImageBackground.Frame;
rectImageBackground.Height = story.CellSize.Height;
controller.ImageBackground.Frame = rectImageBackground;

// Sizing the LabelTitle.
controller.LabelTitle.Frame = new RectangleF(
new PointF(
controller.LabelTitle.Frame.X,
controller.LabelTitle.Frame.Y),
story.TitleSize);

// Sizing the LabelDescription.
controller.LabelDescription.Frame = new RectangleF(
new PointF(
controller.LabelDescription.Frame.X,
story.TitleSize.Height + 10),
story.DescriptionSize);

return cell;
}
[/code]

Hopefully this helps people out. As always you can find me on Twitter via @simongui and I appreciate any retweets!

Tags: , , ,

Trackback from your site.

Simon Guindon

Distributed Systems Engineer

Comments (7)

  • xusan

    |

    thnk great job.
    would you share source code

    Reply

  • filip

    |

    hi, why are you applying a height for the cell in the GetCell method? the height is already set by the overridden delegate, is it?

    thanks for sharing information!

    filip

    Reply

  • Rob Gibbens

    |

    Just wanted to say thank you for sharing what you've learned. It's been invaluable as I plod along behind your path.

    Reply

  • Steve

    |

    Great tip. Trying to follow this but wondering where you get these from:

    - story.CellSize
    - story.TitleSize
    - story.CellSize.Height
    - story.TitleSize.Height
    - story.DescriptionSize
    - story.DescriptionSize.Height

    Would be more helpful to post a working sln for download.

    Reply

  • Steve

    |

    Managed t work this out, supurb bit of work!

    I realised that you store the objects as SizeF types in your data class. Once i did this it all slotted into place. The only other thing I had to do was set the labels as Public in my XIB designer file.

    Reply

  • Steve

    |

    Great examples – much appreciated. If you combine creating Custom Table View Cells with Dynamic Cell height how would you wire that up? It seems that the GetRow Height is called before GetCell?

    Reply

  • zmxmiss

    |

    Thank Very much! in the Story story = viewController.DataSource.Stories.Stories[indexPath.Row]; You did not say " viewController.DataSource.Stories" come from and definition

    Reply

Leave a comment

Startups