Miro Miro - 1 year ago 127
iOS Question

Find final height of UIWebView for vertical stacked layout

I've got a UIView with several containers, each containing 4 items stacked vertically.

  • UILabel - name1 (25px fixed, if present, 0px if not)

  • UIWebView - desc1 (80px fixed to start, but dynamically adjusted for content)

  • UILabel - name2 (25px fixed, if present, 0px if not)

  • UIWebView - desc2 (80px fixed to start, but dynamically adjusted for content)

The UIWebViews contain a short bit of mixed text/html loaded locally in onViewDidLoad. I'm adjusting the frame for each element after I attempt to find the dynamic heights of the webview, which works assuming the height found is accurate. I adjust the size after the webviews have loaded.

I'm having a lot of trouble determining the dynamic height of them accurately however, and thus a lot of overlap is occurring. Often the height is too small and the webview scrolls within its fixed height.

// resizeElements: adjusts all the frame heights/origins
- (void)webViewDidFinishLoad:(UIWebView *)webView {
[self resizeElements];

// not really working at all
// have tried various other methods using html height, etc, etc.
-(float) getWebViewContentSize:(UIWebView*) myWebView{
NSString *heightString = [myWebView stringByEvaluatingJavaScriptFromString: @"document.body.offsetHeight"];
int heightInt = [heightString intValue];
return heightInt;

// seems proportionate, but is about 20% too small for larger amounts of content usually
// and increasingly too small the longer the text gets.
-(float) getWebViewContentSize2:(UIWebView*) myWebView{
CGSize size = myWebView.scrollView.contentSize;
int height = size.height;
return height;

Any better ways to get height or suggestions on laying these 4 elements out otherwise?

Answer Source

had the same issue. cant still find any solution except calling resize function ([self resizeElements] is your case) with half second delay. when calling [myWebView stringByEvaluatingJavaScriptFromString: @"document.body.offsetHeight"] just after webViewDidFinishLoad: - document height is always equal to current webView frame height. it ugly but it working

EDIT: I spent half of hour more and figured out that [webView stringByEvaluatingJavaScriptFromString:@"document.height;"] working properly only if you set webView's frame before you're loading html string. And also important that size of frame must be greater then zero (e.g. CGRectMake(0, 0, 1, 1)) this is not working fine

EDIT 2: I spent one more hour and the final solution is:

layoutSubviews method looks like:

- (void)viewDidLayoutSubviews
    [super viewDidLayoutSubviews];

    CGRect actualFrame = self.view.bounds;

    _titleLabel.frame = CGRectMake(0, 0, actualFrame.size.width, 80);

    [self resizeWebview:_primaryWebView];
    _primaryWebView.frame = CGRectMake(0, _titleLabel.frame.origin.y + _titleLabel.frame.size.height,
                                       actualFrame.size.width, _primaryWebView.frame.size.height);

    _subtitleLabel.frame = CGRectMake(0, _primaryWebView.frame.origin.y + _primaryWebView.frame.size.height,
                                      actualFrame.size.width, 80);

    [self resizeWebview:_secondaryWebView];
    _secondaryWebView.frame = CGRectMake(0, _subtitleLabel.frame.origin.y + _subtitleLabel.frame.size.height,
                                         actualFrame.size.width, _secondaryWebView.frame.size.height);

- (void)resizeWebview:(UIWebView *)webView
    CGRect frame = webView.frame;

    frame.size.height = 1;
    webView.frame = frame;

    frame.size.height = [webView stringByEvaluatingJavaScriptFromString:@"document.height;"].floatValue;

    webView.frame = frame;

you can also get frame height from

CGSize sizeThatFits = [webView sizeThatFits:CGSizeZero];
frame.size = sizeThatFits;

works equally with both methods.

and don't forget to resize webViews after they did load or did change orientation:

- (void)webViewDidFinishLoad:(UIWebView *)webView
    [self.view setNeedsLayout];

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
    [self.view setNeedsLayout];

so, thanks to you, i refactored my code and finally got rid of that ugly time delay :)