1

I have a super ancient Objective-C chat code based on UITableView. There is only one type of cell, let's call it ChatTableViewCell. In our chat there are more than 10 types of messages (text, image, event, etc). ChatTableViewCell itself is responsible for a bunch of logic, such as:

  • Which side to display the avatar on (incoming or outgoing message);
  • Hide avatar if it is 2nd+ message from the same user (message grouping);
  • Reactions to the messages with emojis (users can react to any type of message);
  • Label to show the delivery status for outgoing messages; – etc.

ChatTableViewCell also holds a container view into which a needed subclass of UIView (ImageMessageView, TextMessageView, EventMessageView) is added as a subview.

In func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell method message is taken by its index from the array of messages. Before today, each message object would contain all information, to be displayed in the cell like message text, or, if it is an event message, for example, it would contain all info items such as location name, time, date, number of people who RSVP'ed, etc.

Chat code is an extreme legacy, there is no AutoLayout used anywhere, at all. Each cell height is determined in - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath In this method message is taken by its index and depending on the its type, the message is passed to a static method of the needed subclass of UIView (embedded to the ChatTableViewCell). Those static methods have an ugly manual height calculation like this:

+ (CGSize)neededSizeForContentWithEventInfo:(SBEventInfo *)eventInfo
{
    CGFloat contentHeight = 0;
    
    UILabel *label = [SBEventBubbleView labelForType:LabelTypeIcon];
    label.text = @"";
    CGRect frame = [SBEventBubbleView frameForLabelText:label];
    contentHeight += frame.size.height;
    
    label = [SBEventBubbleView labelForType:LabelTypeTitle];
    label.text = eventInfo.name;
    frame = [SBEventBubbleView frameForLabelText:label];
    contentHeight += frame.size.height;
    
    label = [SBEventBubbleView labelForType:LabelTypeTime];
    label.text = eventInfo.time;
    frame = [SBEventBubbleView frameForLabelText:label];
    contentHeight += frame.size.height;
    
    label = [SBEventBubbleView labelForType:LabelTypeAddress];
    label.text = eventInfo.address;
    frame = [SBEventBubbleView frameForLabelText:label];
    contentHeight += frame.size.height;
    
    contentHeight += kEventBubblePaddingTop + kEventBubblePaddingBottom;
    
    contentHeight += (kEventBubbleContainersSpace + kEventBubbleButtonHeight);
    
    return CGSizeMake([SBEventBubbleView maxWidth], contentHeight);
}

+ (CGRect)frameForLabelText:(UILabel *)label
{
    return [label.text boundingRectWithSize:CGSizeMake([SBEventBubbleView maxWidth] - kEventBubblePaddingLeft - kEventBubblePaddingRight, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:label.font} context:nil];
}

PROBLEM:

Now there is a new type of message that doesn't contain all pieces of info readily available. Instead it only contains some ID, which is passed to a corresponding subclass of UIView, and then inside this view there is a network call, which uses this ID to fetch all the information. Having this asynchronous call, makes it impossible to calculate height of this message on the screen using ugly old-fashioned approach with static methods.

What can I do in this situation? Is it possible to return some temporary/dummy height from

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

and then, when network call in the cell is done, somehow recalculate the height of the cell having all pieces of data available?

Once again, all this ton of code is entirely frame-based, unfortunately there is no AutoLayout used at all. Otherwise, it would be a trivial problem.

Any help would be greatly appreciated! Please ask more information, if you wish to help, but don't fully understand the issue.

realvadim
  • 154
  • 12
  • Solution you are proposing should work. Return temp height and after data is loaded then reload that cell. https://stackoverflow.com/questions/19374699/is-there-a-way-to-update-the-height-of-a-single-uitableviewcell-without-recalcu – k-thorat Aug 17 '20 at 03:14

2 Answers2

0

I don't know how to calculate it when I get the data. But I think any data you get synchronously/asynchronously can be used for calculation, it just needs a callback to tell the controller That I've got the data, and you need to reset cell height. That's my idea. enter image description here

enter image description here

Kris
  • 63
  • 3
0

In tableView(_:heightForRowAt:), return an estimated height for the rows that you don't yet have data for. Once the data is fetched for a row, call tableView.reloadRows(at:with:)

When the table view reloads the row, it will call the tableView(_:heightForRowAt:) delegate method again and you can return the proper height.

Rudedog
  • 4,323
  • 1
  • 23
  • 34