Tyler Rister Tyler Rister - 1 month ago 8
Objective-C Question

UITableViewCell with UIWebview that adjusts height to content size

So I am pulling an HTML string down from an API and I am trying to load it into a UIWebView that is in a UITableViewCell and the content scrolls in the cell. I need to have the webview's height adjust to the size of the content and then adjust the cell accordingly. I found a post here in swift:
UITableViewCell With UIWebView Dynamic Height
And I tried doing the same thing but I am having some trouble because I am using a NSMutableArray to store a NSNumber with float and then trying to load it in but cellForRowAtIndexPath and heightForRowAtIndexPath both execute before the webview delegate method gets called so I will get an index out of bounds error.

So far I have this:

@interface FGCThreadDetailTableViewController ()

@property (copy, nonatomic) NSArray<FGCThreadModel*>* threads;
@property (copy, nonatomic) NSMutableArray* contentHeights;

@end

@implementation FGCThreadDetailTableViewController

- (void)viewDidLoad {
[super viewDidLoad];
[self.tableView registerClass:[FGCThreadDetailCell class] forCellReuseIdentifier:@"threadDetailCell"];
self.tableView.estimatedRowHeight = 100;
self.tableView.rowHeight = UITableViewAutomaticDimension;
[SVProgressHUD show];
[[FGCThread postsByThreadID:_threadID] subscribeNext:^(id x) {
_threads = x;
[SVProgressHUD dismiss];
[self.tableView reloadData];
}];
_contentHeights = [[NSMutableArray alloc] init];
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _threads.count;
}

- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
FGCThreadDetailCell* cell = [tableView dequeueReusableCellWithIdentifier:@"threadDetailCell" forIndexPath:indexPath];
FGCThreadModel* thread = _threads[indexPath.row];
cell.webView.delegate = self;
CGFloat htmlHeight;
htmlHeight = [_contentHeights[indexPath.row] floatValue];
cell.webView.tag = indexPath.row;
[cell.webView loadHTMLString:thread.postBodyHTMLString baseURL:nil];
cell.webView.frame = CGRectMake(0, 0, cell.frame.size.width, htmlHeight);
return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return [_contentHeights[indexPath.row] floatValue];
}

- (void)webViewDidFinishLoad:(UIWebView *)webView {
if (_contentHeights[webView.tag]) {
return;
}
_contentHeights[webView.tag] = [NSNumber numberWithFloat:[[webView stringByEvaluatingJavaScriptFromString:@"document.body.offsetHeight;"] floatValue]];
[self.tableView reloadData];
}

@end


Thanks in advance!

Answer

The auto layout approach might be tricky. An easier approach is to just set the frame of the corresponding cell in webViewDidFinishLoad. You can use cellForRowAtIndexPath on the table view to get the displayed cell (it won't try to get the cell from the UITableViewDataSource if it's already displayed because it'll be cached).

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    if (_contentHeights[webView.tag]) {
        return;
    }
    float height = [NSNumber numberWithFloat:[[webView stringByEvaluatingJavaScriptFromString:@"document.body.offsetHeight;"] floatValue]];

    NSIndexPath* indexOfCell = [NSIndexPath indexPathForRow:webView.tag inSection:0];
    UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:indexOfCell];
    cell.frame = CGRectMake(0, 0, cell.frame.size.width, height);
}
Comments