Dynamic UITableView Cell Height Based on Contents

前端 未结 12 630
佛祖请我去吃肉
佛祖请我去吃肉 2020-12-05 07:29

I have a UITableView that is populated with custom cells (inherited from UITableViewCell), each cell contains a UIWebView that is auto

相关标签:
12条回答
  • 2020-12-05 07:36

    Here is code that I used for dynamic cell height when fetching tweets from twitter and then storing them in CoreData for offline reading.

    Not only does this show how to get the cell and data content, but also how to dynamically size a UILabel to the content with padding

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    
        Tweet *tweet = [self.fetchedResultsController objectAtIndexPath:indexPath];
    
        NSString* text = tweet.Text;
    
        TweetTableViewCell *cell = (TweetTableViewCell*)[self tableView:tableView cellForRowAtIndexPath:indexPath];
    
        //Set the maximum size
        CGSize maximumLabelSize = cell.tweetLabel.frame.size;
        CGPoint originalLocation = cell.tweetLabel.frame.origin;
    
        //Calculate the new size based on the text
        CGSize expectedLabelSize = [text sizeWithFont:cell.tweetLabel.font constrainedToSize:maximumLabelSize lineBreakMode:cell.tweetLabel.lineBreakMode];
    
    
        //Dynamically figure out the padding for the cell
        CGFloat topPadding = cell.tweetLabel.frame.origin.y - cell.frame.origin.y;
    
    
        CGFloat bottomOfLabel = cell.tweetLabel.frame.origin.y + cell.tweetLabel.frame.size.height;
        CGFloat bottomPadding = cell.frame.size.height - bottomOfLabel;
    
    
        CGFloat padding = topPadding + bottomPadding;
    
    
        CGFloat topPaddingForImage = cell.profileImage.frame.origin.y - cell.frame.origin.y;
        CGFloat minimumHeight = cell.profileImage.frame.size.height + topPaddingForImage + bottomPadding;
    
        //adjust to the new size
        cell.tweetLabel.frame = CGRectMake(originalLocation.x, originalLocation.y, cell.tweetLabel.frame.size.width, expectedLabelSize.height);
    
    
        CGFloat cellHeight = expectedLabelSize.height + padding;
    
        if (cellHeight < minimumHeight) {
    
            cellHeight = minimumHeight;
        }
    
        return cellHeight;
    }
    
    0 讨论(0)
  • 2020-12-05 07:40

    I tried many solutions, but the one that worked was this, suggested by a friend:

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    
        int height = [StringUtils findHeightForText:yourLabel havingWidth:yourWidth andFont:[UIFont systemFontOfSize:17.0f]];
    
        height += [StringUtils findHeightForText:yourOtherLabel havingWidth:yourWidth andFont:[UIFont systemFontOfSize:14.0f]];
    
        return height + CELL_SIZE_WITHOUT_LABELS; //important to know the size of your custom cell without the height of the variable labels
    }
    

    The StringUtils.h class:

    #import <Foundation/Foundation.h>
    
    @interface StringUtils : NSObject
    
    + (CGFloat)findHeightForText:(NSString *)text havingWidth:(CGFloat)widthValue andFont:(UIFont *)font;
    
    @end
    

    StringUtils.m class:

    #import "StringUtils.h"
    
    @implementation StringUtils
    
    + (CGFloat)findHeightForText:(NSString *)text havingWidth:(CGFloat)widthValue andFont:(UIFont *)font {
        CGFloat result = font.pointSize+4;
        if (text) {
            CGSize size;
    
            CGRect frame = [text boundingRectWithSize:CGSizeMake(widthValue, CGFLOAT_MAX)
                                              options:NSStringDrawingUsesLineFragmentOrigin
                                           attributes:@{NSFontAttributeName:font}
                                              context:nil];
            size = CGSizeMake(frame.size.width, frame.size.height+1);
            result = MAX(size.height, result); //At least one row
        }
        return result;
    }
    
    @end
    

    It worked perfectly for me. I had a Custom Cell with 3 images with fixed sizes, 2 labels with fixed sizes and 2 variable labels.

    0 讨论(0)
  • 2020-12-05 07:40

    This is one of my nice solution. it's worked for me.

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        cell.textLabel.text = [_nameArray objectAtIndex:indexPath.row];
        cell.textLabel.numberOfLines = 0;
        cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
    }
    
    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
        return UITableViewAutomaticDimension;
    }
    

    We need to apply these 2 changes.

    1)cell.textLabel.numberOfLines = 0;
      cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
    
    2)return UITableViewAutomaticDimension;
    
    0 讨论(0)
  • 2020-12-05 07:43

    The best way that I've found for dynamic height is to calculate the height beforehand and store it in a collection of some sort (probably an array.) Assuming the cell contains mostly text, you can use -[NSString sizeWithFont:constrainedToSize:lineBreakMode:] to calculate the height, and then return the corresponding value in heightForRowAtIndexPath:

    If the content is constantly changing, you could implement a method that updated the array of heights when new data was provided.

    0 讨论(0)
  • 2020-12-05 07:45

    I always implement this in all my cells in a super cell class because for some reason UITableViewAutomaticDimension doesn't work so well.

    -(CGFloat)cellHeightWithData:(id)data{
        CGFloat height = [[self contentView] systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
        [self fillCellWithData:data]; //set the label's text or anything that may affect the size of the cell
        [self layoutIfNeeded];
        height = [[self contentView] systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
        return height+1; //must add one because of the cell separator
    }
    

    just call this method on your -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPathusing a dummy cell.

    note: this works only with autolayout, but it also works with ios 7 and later.

    pd: don't forget to check the checkbox on the xib or storyboard for "preferred width explicit" and set the static width (on the cmd + alt + 5 menu)

    0 讨论(0)
  • 2020-12-05 07:52

    This usually works pretty well:

    Objective-C:

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
        return UITableViewAutomaticDimension;
    }
    

    Swift:

    override func tableView(tableView: UITableView!, heightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat {
        return UITableViewAutomaticDimension;
    }
    
    0 讨论(0)
提交回复
热议问题