Custom Cell Row Height setting in storyboard is not responding

114,348

Solution 1

On dynamic cells, rowHeight set on the UITableView always overrides the individual cells' rowHeight.

But on static cells, rowHeight set on individual cells can override UITableView's.

Not sure if it's a bug, Apple might be intentionally doing this?

Solution 2

If you use UITableViewController, implement this method:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;

In the function of a row you can choose Height. For example,

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.row == 0) {
       return 100;
    } 
    else {
       return 60;
    }
}

In this exemple, the first row height is 100 pixels, and the others are 60 pixels.

I hope this one can help you.

Solution 3

For dynamic cells, rowHeight set on the UITableView always overrides the individual cells' rowHeight.

This behavior is, IMO, a bug. Anytime you have to manage your UI in two places it is prone to error. For example, if you change your cell size in the storyboard, you have to remember to change them in the heightForRowAtIndexPath: as well. Until Apple fixes the bug, the current best workaround is to override heightForRowAtIndexPath:, but use the actual prototype cells from the storyboard to determine the height rather than using magic numbers. Here's an example:

- (CGFloat)tableView:(UITableView *)tableView 
           heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    /* In this example, there is a different cell for
       the top, middle and bottom rows of the tableView.
       Each type of cell has a different height.
       self.model contains the data for the tableview 
    */
    static NSString *CellIdentifier;
    if (indexPath.row == 0) 
        CellIdentifier = @"CellTop";
    else if (indexPath.row + 1 == [self.model count] )
        CellIdentifier = @"CellBottom";
    else
        CellIdentifier = @"CellMiddle";

    UITableViewCell *cell = 
              [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    return cell.bounds.size.height;
}

This will ensure any changes to your prototype cell heights will automatically be picked up at runtime and you only need to manage your UI in one place: the storyboard.

Solution 4

I've built the code the various answers/comments hint at so that this works for storyboards that use prototype cells.

This code:

  • Does not require the cell height to be set anywhere other than the obvious place in the storyboard
  • Caches the height for performance reasons
  • Uses a common function to get the cell identifier for an index path to avoid duplicated logic

Thanks to Answerbot, Brennan and lensovet.

- (NSString *)cellIdentifierForIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellIdentifier = nil;

    switch (indexPath.section)
    {
        case 0:
            cellIdentifier = @"ArtworkCell";
            break;
         <... and so on ...>
    }

    return cellIdentifier;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellIdentifier = [self cellIdentifierForIndexPath:indexPath];
    static NSMutableDictionary *heightCache;
    if (!heightCache)
        heightCache = [[NSMutableDictionary alloc] init];
    NSNumber *cachedHeight = heightCache[cellIdentifier];
    if (cachedHeight)
        return cachedHeight.floatValue;

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    CGFloat height = cell.bounds.size.height;
    heightCache[cellIdentifier] = @(height);
    return height;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellIdentifier = [self cellIdentifierForIndexPath:indexPath];

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];

    <... configure cell as usual...>

Solution 5

There are actually two places where you need to change to row height, first the cell (you already did change that) and now select the Table View and check the Size Inspector

Share:
114,348

Related videos on Youtube

zirinisp
Author by

zirinisp

Updated on January 05, 2020

Comments

  • zirinisp
    zirinisp over 4 years

    I am trying to adjust the cell height for one of the cells on my table view. I am adjusting the size from the "row height" setting inside the "size inspector" of the cell in question. When I run the app on my iPhone the cell has the default size set from the "row size" in the table view.

    If I change the "row size" of the table view then the size of all cells changes. I do not want to do that as I want a custom size only for one cell. I have seen a lot of posts that have a programmatic solution to the problem, but I would prefer to do it through storyboard, if that is possible.

  • zirinisp
    zirinisp over 12 years
    I right clicked and selected "open as" -> "source code". The rowHeight was already set to 120. I believe that storyboard has a bug there and it ignores custom cell height.
  • Rhubarb
    Rhubarb over 11 years
    Right answer #3, and specifically because this answer applies to Interface Builder/Storyboards. If you select the cell in IB, then the Size Inspector shows Row Height at the top (with a "custom" checkbox), but if you select the whole table view the Size Inspector shows Row Height at the top there too (no "custom" in this case). As pixelfreak says, only the table view setting is used for dynamic cells. (Not sure if it's intentional)
  • marciokoko
    marciokoko over 11 years
    Beber, does one ALWAYS have to do both (check Custom and edit the Row Height value in storyboard && specify it in the heightForRowAtIndexPath)?
  • JosephH
    JosephH almost 11 years
    I've posted an answer with full code including the caching; see stackoverflow.com/a/16881312/292166
  • daspianist
    daspianist almost 11 years
    The addition of this method worked very well, and is now keeping all the customization in the storyboard as it should belong.
  • abbood
    abbood over 10 years
    this answer implies that the solution would be simply to change the UITableView content from Dynamic Prototypes to Static Cells, I did that, and my whole project blew up.. almost got killed myself.
  • Biclops
    Biclops over 10 years
    Is there any way to retrieve this number? Is the only way of digging deep it up is to diving into the storyboard and pulling it out from there?
  • Vincent Bernier
    Vincent Bernier over 10 years
    It is definitely NOT a bug, because your table is Dynamic, so how could the system know where your gone a use each of those cells ? And how many times each prototype would be used.
  • sbaker
    sbaker almost 10 years
    I know this is an old answer, but does anybody else find that this results in a stack overflow? When the view loads, I get UISectionRowData refreshWithSection:tableViewRowData: which calls tableView:heightForRowAtIndexPath: which calls dequeueReusableCellWithIdentifier: which calls [UISectionRowData ...] so I have a recursive stack overflow. I really wanted to use this solution, but it doesn't seem to work with iOS7.
  • skywinder
    skywinder almost 10 years
    Wow-wow-wow! I upvoted answer, but be AWARE! This method causes non only performance penalty as said @Brennan in comments, it also cause growing memory allocation at each reloadData something like memory leak! It's needed to use lensovet's workaround above! Spend a day to catch this memory leak!
  • Eric G
    Eric G over 9 years
    There seems to also be an issue if you initially setup a table view as "static cells" and then change it to "dynamic prototypes." I had an issue where even the delegate method for rowHeight was being ignored. I first resolved it by directly editing the storyboard XML, then finally just rebuilt the storyboard scene from scratch, using dynamic prototypes from the start.
  • King-Wizard
    King-Wizard almost 9 years
    Does not work in Swift, because cell.bounds.size.height always returns 0.0
  • King-Wizard
    King-Wizard almost 9 years
    Does not work in Swift, because cell.bounds.size.height always returns 0.0
  • King-Wizard
    King-Wizard almost 9 years
    The cells don't exist yet at the time the system called your tableView:heightForRowAtIndexPath method. You are supposed to be able to use the indexPath that's passed to you to index into your data model and see what kind of cell will be used there, and then calculate the height for that cell and return it. That enables the system to lay out the cells in the table before it creates any cells.
  • King-Wizard
    King-Wizard almost 9 years
    Also, you should not call your own cellForRowAtIndexPath method. Table views have a cellForRowAtIndexPath method that will return a cell for that index path if it is currently visible on the screen, but often that index path does not have a cell associated with it.
  • King-Wizard
    King-Wizard almost 9 years
    The cells don't exist yet at the time the system called your tableView:heightForRowAtIndexPath method. You are supposed to be able to use the indexPath that's passed to you to index into your data model and see what kind of cell will be used there, and then calculate the height for that cell and return it. That enables the system to lay out the cells in the table before it creates any cells.
  • King-Wizard
    King-Wizard almost 9 years
    Also, you should not call your own cellForRowAtIndexPath method. Table views have a cellForRowAtIndexPath method that will return a cell for that index path if it is currently visible on the screen, but often that index path does not have a cell associated with it.
  • JosephH
    JosephH almost 9 years
    @snow-tiger I've not tried this in swift, but I don't understand your comments. I don't 'call [my] own cellForRowAtIndexPath method', and it is deliberate that I don't. I'm also aware that the cells don't exist at the time tableView:heightForRowAtIndexPath is called, that is the whole point of this code, and why it uses dequeueReusableCellWithIdentifier to get a cell. The code that is posted in this answer works. Either there is something extra going on in swift, there's something not right in the swift conversion, or are trying to solve a different problem that this question/answer targets.
  • King-Wizard
    King-Wizard almost 9 years
    Maybe it works in Objective-C but it doesn't work in Swift using iOS 8.3. Why? Simple as hell, because heightForRowAtIndexPath is called multiple times before tableView:cellForRowAtIndexPath: and dequeueReusableCellWithIdentifier returns the cell displayed on the screen, but the cell is not drawn at that moment, so that's why it returns 0.0 for the height of the cell. So this snippet doesn't work in Swift sorry. I am still obliged to use predefined magic numbers for each prototype cell.
  • King-Wizard
    King-Wizard almost 9 years
    Run the following code in Swift using two dynamic (prototype) cells, in order to reproduce the beug: pastebin.com/cX7LbCru
  • natur3
    natur3 almost 9 years
    @pixelfreak this works for me on dynamic cells. Initially I was adding a subview to the tableview and had the row height set to AutoDemension, I changed it to an arbitrary value to fit the max data returned.
  • Danny
    Danny over 8 years
    This is all fine and good but unless I am misreading the code, it simply relies on magic numbers and completely ignores whatever value you set in IB for the dynamic cells so...not exactly a solution to OP's question
  • Nikita P
    Nikita P almost 8 years
    There is another edge to this problem, Static table view cells are only valid when embedded in a UITableViewController instance. This can then be solved with Henrik's answer.
  • Ostmeistro
    Ostmeistro over 7 years
    Could you help me regarding this? I need to have dynamic cells with dynamic height, but if I use this method, no matter what I return all cells dissapear instead. Except on iPhone 5 the device, where it works as expected. Can you understand why?
  • Andy Chou
    Andy Chou about 7 years
    This works, but more generally you can do: cell.sizeToFit(); self.tableView.rowHeight = cell.frame.height
  • xaphod
    xaphod about 7 years
    Calling dequeueReusableCellWithIdentifier from within heightForRowAtIndexPath == infinite recursion for me
  • iman kazemayni
    iman kazemayni over 6 years
    in tableview(not cellview) > in size inspector tab > row height
  • Fayçal
    Fayçal over 6 years
    Hi, please how did you do that?
  • Justin
    Justin over 5 years
    You just need to add a constraint set to the bottom of the cell.
  • Scooter
    Scooter about 5 years
    Drives me nuts each time I forget this detail. No idea why adjusting the cell doesn't update the tableView automatically when using storyboards!
  • TheJeff
    TheJeff almost 5 years
    By far the best solution
  • David
    David over 4 years
    When I do this and select "automatic" in the storyboard for cell height, it wants to set all heights to 44 in the storyboard although it appears correct when I run. How can I get storyboard to display correctly?