Question

Preface: this is quite an abstract question.

I've got some cells in a UITableView that use a different image depending on the height of the cell (in 50px increments, from 50px up to 1000px high). I'm hitting a weird issue where when the app first loads, cellForRowAtIndexPath reports that the first three cells (regardless of whether or not all three are onscree) have a height of 100px, then when I scroll down and back up again, their height reverts to the correct value. There's no onscreen change of height, only the image used changes, and the value of cell.frame.size.height in my NSLog statements.

I've been logging in heightForRowAtIndexPath and cellForRowAtIndexPath, and I can verify that the heights are correct when they leave heightForRowAtIndexPath. But, when entering cellForRowAtIndexPath, those first three heights are all set uniformly to 100. I then scroll down until those three cells are off-screen, then scroll back up, and the .height value has changed to the correct value.

To summarise, my questions are:

Is there any code called between the very end of heightForRowAtIndexPath and the very beginning of cellForRowAtIndexPath that could be changing the cell.frame.size.height value of these first few cells?

Why would this issue only be occurring on the first THREE cells? And, why would they revert when reloaded a moment later? I thought the reuse identifier could be the cause, but they're still assigned that identifier when first loaded, so that shouldn't matter, should it?

EDIT: I think the 100px height is coming from the row height of the prototype cell in my storyboard, as if I change that to 101px, those first three cells are then initially at 101px.

EDIT: Here's the code I'm using to set the image, though this part is working, it's just got the wrong value to work with:

UIImage *cappedBG;
float cellHeight = cell.contentView.frame.size.height;

if (cellHeight <= 51) {
    cappedBG = [[UIImage imageNamed: @"bg50.png"] resizableImageWithCapInsets: UIEdgeInsetsMake(25, 25, 25, 25) resizingMode: UIImageResizingModeStretch];
} else if (cellHeight <= 101) {
    cappedBG = [[UIImage imageNamed: @"bg100.png"] resizableImageWithCapInsets: UIEdgeInsetsMake(25, 25, 25, 25) resizingMode: UIImageResizingModeStretch];
} else if (cellHeight <= 151) {
    cappedBG = [[UIImage imageNamed: @"bg150.png"] resizableImageWithCapInsets: UIEdgeInsetsMake(25, 25, 25, 25) resizingMode: UIImageResizingModeStretch];
} else if (cellHeight <= 201) {
    cappedBG = [[UIImage imageNamed: @"bg200.png"] resizableImageWithCapInsets: UIEdgeInsetsMake(25, 25, 25, 25) resizingMode: UIImageResizingModeStretch];
} else {
    cappedBG = [[UIImage imageNamed: @"bg250.png"] resizableImageWithCapInsets: UIEdgeInsetsMake(25, 25, 25, 25) resizingMode: UIImageResizingModeStretch];
} 
Was it helpful?

Solution

You just can't rely on the frame of the cell in tableView:cellForRowAtIndexPath:. The frame of the cell you configure in that method will be set after the code has returned from this method.

The values you get are not the actual heights of that particular row. You get the height from the cell that has just been dequeued. If there is no cell to dequeue the height from the cell that was just created is used. And 100 is probably the height of the cell in your storyboard/nib.
This height has nothing to do with the height of the cell that will be displayed.

Since you calculate the row height in tableView:heightForRowAtIndexPath: already, use this value.

float cellHeight = [self tableView:tableView heightForRowAtIndexPath:indexPath];

if (cellHeight <= 51) {
    cappedBG = [[UIImage imageNamed: @"bg50.png"] resizableImageWithCapInsets: UIEdgeInsetsMake(25, 25, 25, 25) resizingMode: UIImageResizingModeStretch];
...

OTHER TIPS

You shouldn't really be doing layout of a cell in cellForRowAtIndexPath.

The best way to do this is to create a UITableViewCell subclass with all the UI elements you need.

Then in the subclass you can override - (void)layoutSubviews.

This way you keep everything separate rather than putting EVERYTHING in the view controller.

Currently, after looking at your question, I think you are having trouble with different cell heights (when scrolled). I believe you have not given correct values in heightForRowAtIndexPath.

Following is the simple code that I have written. This code has displays 20 rows in the table and alternate row has different size.(I have used different colours to distinguish)

#pragma mark - TableView DataSource Methods

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

// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}


// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 20;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *stringCellIdentifier = @"BookOutVehicleCell";

    UITableViewCell *tableViewCell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:stringCellIdentifier];

    if (tableViewCell == nil)
        tableViewCell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:stringCellIdentifier] autorelease];

    if(indexPath.row %2 == 0) tableViewCell.contentView.backgroundColor = [UIColor redColor];
    else  tableViewCell.contentView.backgroundColor = [UIColor greenColor];

       return tableViewCell;

}

#pragma mark - TableView Delegate Methods
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];

}

This is the output.

enter image description here

In this case, even if we scroll the tableview up and down, rows height does not change.

Hope this helps. And sorry If I have misunderstood your question. It would be nice if you provide a code snippet. It will give us better idea about the issue.

EDIT: As per my understanding of the issue you are getting correct heights for each row/cell, however you have used an imageview in cell which will display different images as per the height of the cell. Check if following works for you:

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *stringCellIdentifier = @"BookOutVehicleCell";

        UITableViewCell *tableViewCell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:stringCellIdentifier];

        if (tableViewCell == nil)
            tableViewCell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:stringCellIdentifier] autorelease];

       //Check your height here and use appropriate image 
       if(cell.contentView.frame.size.height <100)
       {
            image = "xxx.png";
       }
       else
       if(cell.contentView.frame.size.height >=100 && cell.contentView.frame.size.height <200)
       {
           image = "yyy.png";
       }


    }

Thanks.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top