如何确定 UIWebView 的内容大小?

我有一个 UIWebView与不同(单页)的内容。我想找出内容的 CGSize来适当调整父视图的大小。不幸的是,显然的 -sizeThatFits:只返回了 webView 当前的帧大小。

130219 次浏览

AFAIK you can use [webView sizeThatFits:CGSizeZero] to figure out it's content size.

事实证明,我使用 -sizeThatFits:的第一个猜测并非完全错误。它似乎工作,但前提是 webView 的帧在发送 -sizeThatFits:之前被设置为最小尺寸。然后,我们可以纠正错误的帧尺寸的拟合大小。这听起来很糟糕,但实际上并没有那么糟糕。因为我们两次都是在对方之后进行帧更改,所以视图不会更新,也不会闪烁。

当然,我们必须等待内容被加载,所以我们将代码放入 -webViewDidFinishLoad:委托方法中。

Obj-C

- (void)webViewDidFinishLoad:(UIWebView *)aWebView {
CGRect frame = aWebView.frame;
frame.size.height = 1;
aWebView.frame = frame;
CGSize fittingSize = [aWebView sizeThatFits:CGSizeZero];
frame.size = fittingSize;
aWebView.frame = frame;


NSLog(@"size: %f, %f", fittingSize.width, fittingSize.height);
}

斯威夫特4. x

func webViewDidFinishLoad(_ webView: UIWebView) {
var frame = webView.frame
frame.size.height = 1
webView.frame = frame
let fittingSize = webView.sizeThatFits(CGSize.init(width: 0, height: 0))
frame.size = fittingSize
webView.frame = frame
}

I should point out there's 另一种方法 (thanks @GregInYEG) using JavaScript. Not sure which solution performs better.

我更喜欢这个方案。

重提这个问题,因为我发现奥特温的答案只有工作的大部分时间..。

webViewDidFinishLoad方法可能被调用不止一次,而 sizeThatFits返回的第一个值只是最终大小的一部分。然后,不管出于什么原因,当 webViewDidFinishLoad再次触发时对 sizeThatFits的下一次调用将错误地返回与之前相同的值!对于相同的内容,这种情况会随机发生,就好像它是某种并发性问题一样。也许这种行为已经随着时间的推移而改变了,因为我正在为 iOS5构建系统,并且发现 sizeToFit的工作原理大致相同(尽管以前没有这样做?)

我已经决定了这个简单的解决方案:

- (void)webViewDidFinishLoad:(UIWebView *)aWebView
{
CGFloat height = [[aWebView stringByEvaluatingJavaScriptFromString:@"document.height"] floatValue];
CGFloat width = [[aWebView stringByEvaluatingJavaScriptFromString:@"document.width"] floatValue];
CGRect frame = aWebView.frame;
frame.size.height = height;
frame.size.width = width;
aWebView.frame = frame;
}

斯威夫特(2.2) :

func webViewDidFinishLoad(webView: UIWebView) {


if let heightString = webView.stringByEvaluatingJavaScriptFromString("document.height"),
widthString = webView.stringByEvaluatingJavaScriptFromString("document.width"),
height = Float(heightString),
width = Float(widthString) {


var rect = webView.frame
rect.size.height = CGFloat(height)
rect.size.width = CGFloat(width)
webView.frame = rect
}
}

更新: 我已经发现,正如在评论中提到的,这似乎没有捕捉到内容缩水的情况。不确定它是否适用于所有内容和操作系统版本,尝试一下。

我有另一个很好的解决方案。

On one hand, Ortwin's approach & solution works only with iOS 6.0 and later, but fails to work correctly on iOS 5.0, 5.1 and 5.1.1, and on the other hand there is something that I don't like and can't understand with Ortwin's approach, it's the use of the method [webView sizeThatFits:CGSizeZero] with the parameter CGSizeZero : If you read Apple Official documentation about this methods and its parameter, it says clearly :

此方法的默认实现返回视图边界矩形的大小部分。子类可以重写此方法,以根据所需的任何子视图布局返回自定义值。例如,UISswitch 对象返回一个固定大小值,该值表示开关视图的标准大小,而 UIImageView 对象返回它当前显示的图像的大小。

我的意思是,就好像他在没有任何逻辑的情况下找到了他的解决方案,因为阅读文档时,传递给 [webView sizeThatFits: ...]的参数至少应该具有所需的 width。在他的解决方案中,在使用 CGSizeZero参数调用 sizeThatFits之前,将所需的宽度设置为 webView的帧。所以我认为这个解决方案在 iOS6上运行是“偶然”的。

我设想了一种更合理的方法,它的优点是适用于 iOS 5.0及更高版本... ... 同时也适用于多个 webView (其属性 webView.scrollView.scrollEnabled = NO嵌入在 scrollView中)的复杂情况。

下面是我的代码,用于强制将 webView的布局设置为所需的 width,并将相应的 height设置回 webView本身:

Obj-C

- (void)webViewDidFinishLoad:(UIWebView *)aWebView
{
aWebView.scrollView.scrollEnabled = NO;    // Property available in iOS 5.0 and later
CGRect frame = aWebView.frame;


frame.size.width = 200;       // Your desired width here.
frame.size.height = 1;        // Set the height to a small one.


aWebView.frame = frame;       // Set webView's Frame, forcing the Layout of its embedded scrollView with current Frame's constraints (Width set above).


frame.size.height = aWebView.scrollView.contentSize.height;  // Get the corresponding height from the webView's embedded scrollView.


aWebView.frame = frame;       // Set the scrollView contentHeight back to the frame itself.
}

斯威夫特4. x

func webViewDidFinishLoad(_ aWebView: UIWebView) {


aWebView.scrollView.isScrollEnabled = false
var frame = aWebView.frame


frame.size.width = 200
frame.size.height = 1


aWebView.frame = frame
frame.size.height = aWebView.scrollView.contentSize.height


aWebView.frame = frame;
}

请注意,在我的例子中,webView是嵌入在一个定制的 scrollView有其他的 webViews... 所有这些 webViews有他们的 webView.scrollView.scrollEnabled = NO,和最后一段代码,我必须添加的是计算的 heightcontentSize的我的定制 scrollView嵌入这些 webViews,但它是一样容易的总结我的 webViewscrollView0计算的技巧上述..。

我使用一个不是子视图(因此不是窗口层次结构的一部分)的 UIWebView来确定 UITableViewCells的 HTML 内容的大小。我发现断开的 UIWebView没有正确地报告它的大小与 -[UIWebView sizeThatFits:]。此外,正如在 https://stackoverflow.com/a/3937599/9636中提到的,您必须将 UIWebViewframe height设置为1,以便获得适当的高度。

如果 UIWebView的高度太大(例如,你把它设置为1000,但是 HTML 内容的大小只有500) :

UIWebView.scrollView.contentSize.height
-[UIWebView stringByEvaluatingJavaScriptFromString:@"document.height"]
-[UIWebView sizeThatFits:]

返回 height为1000。

To solve my problem in this case, I used https://stackoverflow.com/a/11770883/9636, which I dutifully voted up. However, I only use this solution when my UIWebView.frame.width is the same as the -[UIWebView sizeThatFits:] width.

一个简单的解决方案就是使用 webView.scrollView.contentSize,但我不知道这是否适用于 JavaScript。如果没有使用 JavaScript,那么可以肯定的是:

- (void)webViewDidFinishLoad:(UIWebView *)aWebView {
CGSize contentSize = aWebView.scrollView.contentSize;
NSLog(@"webView contentSize: %@", NSStringFromCGSize(contentSize));
}

同样在 iOS7中,为了使所有提到的方法都能正常工作,请在视图控制器 viewDidLoad方法中添加以下内容:

if ([self respondsToSelector:@selector(automaticallyAdjustsScrollViewInsets)]) {
self.automaticallyAdjustsScrollViewInsets = NO;
}

Otherwise neither of methods would work as it should.

None of the suggestions here helped me with my situation, but I read something that did give me an answer. I have a ViewController with a fixed set of UI controls followed by a UIWebView. I wanted the entire page to scroll as though the UI controls were connected to the HTML content, so I disable scrolling on the UIWebView and must then set the content size of a parent scroll view correctly.

重要的提示是,UIWebView 在呈现到屏幕上之前不会正确地报告其大小。因此,当我加载内容时,我将内容大小设置为可用的屏幕高度。然后,在 viewDidAppear 中,我将 scrollview 的内容大小更新为正确的值。这对我很有用,因为我在本地内容上调用 loadHTMLString。如果使用 loadRequest,可能还需要更新 webViewDidFinishLoad 中的 contentSize,具体取决于检索 html 的速度。

没有闪烁,因为只有滚动视图的不可见部分被更改。

我也被这个问题困住了,然后我意识到如果我想计算 webView 的动态高度,我需要首先告诉 webView 的宽度,所以我在 js 前面加了一行,结果我可以得到非常精确的实际高度。

代码很简单:

-(void)webViewDidFinishLoad:(UIWebView *)webView
{


//tell the width first
webView.width = [UIScreen mainScreen].bounds.size.width;


//use js to get height dynamically
CGFloat scrollSizeHeight = [[webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight"] floatValue];
webView.height = scrollSizeHeight;


webView.x = 0;
webView.y = 0;


//......
}

如果你的 HTML 包含像 iframe 一样的 heavy HTML 内容(比如 facebook,twitter,instagram) ,那么真正的解决方案要困难得多,首先包装你的 HTML:

[htmlContent appendFormat:@"<html>", [[LocalizationStore instance] currentTextDir], [[LocalizationStore instance] currentLang]];
[htmlContent appendFormat:@"<head>"];
[htmlContent appendString:@"<script type=\"text/javascript\">"];
[htmlContent appendFormat:@"    var lastHeight = 0;"];
[htmlContent appendFormat:@"    function updateHeight() { var h = document.getElementById('content').offsetHeight; if (lastHeight != h) { lastHeight = h; window.location.href = \"x-update-webview-height://\" + h } }"];
[htmlContent appendFormat:@"    window.onload = function() {"];
[htmlContent appendFormat:@"        setTimeout(updateHeight, 1000);"];
[htmlContent appendFormat:@"        setTimeout(updateHeight, 3000);"];
[htmlContent appendFormat:@"        if (window.intervalId) { clearInterval(window.intervalId); }"];
[htmlContent appendFormat:@"        window.intervalId = setInterval(updateHeight, 5000);"];
[htmlContent appendFormat:@"        setTimeout(function(){ clearInterval(window.intervalId); window.intervalId = null; }, 30000);"];
[htmlContent appendFormat:@"    };"];
[htmlContent appendFormat:@"</script>"];
[htmlContent appendFormat:@"..."]; // Rest of your HTML <head>-section
[htmlContent appendFormat:@"</head>"];
[htmlContent appendFormat:@"<body>"];
[htmlContent appendFormat:@"<div id=\"content\">"]; // !important https://stackoverflow.com/a/8031442/1046909
[htmlContent appendFormat:@"..."]; // Your HTML-content
[htmlContent appendFormat:@"</div>"]; // </div id="content">
[htmlContent appendFormat:@"</body>"];
[htmlContent appendFormat:@"</html>"];

然后将处理 X-update-webview-height方案添加到您的 应该启动 LoadWithRequest:

    if (navigationType == UIWebViewNavigationTypeLinkClicked || navigationType == UIWebViewNavigationTypeOther) {
// Handling Custom URL Scheme
if([[[request URL] scheme] isEqualToString:@"x-update-webview-height"]) {
NSInteger currentWebViewHeight = [[[request URL] host] intValue];
if (_lastWebViewHeight != currentWebViewHeight) {
_lastWebViewHeight = currentWebViewHeight; // class property
_realWebViewHeight = currentWebViewHeight; // class property
[self layoutSubviews];
}
return NO;
}
...

最后在 视图中添加以下代码:

    ...
NSInteger webViewHeight = 0;


if (_realWebViewHeight > 0) {
webViewHeight = _realWebViewHeight;
_realWebViewHeight = 0;
} else {
webViewHeight = [[webView stringByEvaluatingJavaScriptFromString:@"document.getElementById(\"content\").offsetHeight;"] integerValue];
}


upateWebViewHeightTheWayYorLike(webViewHeight);// Now your have real WebViewHeight so you can update your webview height you like.
...

另外,你可以在你的 Objective C/Swift 代码中实现延迟时间(SetTimeout 和 setInterval)——这取决于你。

关于 UIWebView 和 Facebook 嵌入的重要信息: 嵌入式 Facebook 帖子在 UIWebView 中显示不正确

当在 scrollview 的某个地方使用 webview 作为子视图时,你可以将高度限制设置为某个常量值,然后从中创建出口,并使用如下:

- (void)webViewDidFinishLoad:(UIWebView *)webView {
webView.scrollView.scrollEnabled = NO;
_webViewHeight.constant = webView.scrollView.contentSize.height;
}

太奇怪了!

我测试的解决方案 sizeThatFits:[webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight"]都是 NOT为我工作。

然而,我发现了一个有趣的简单的方法来获得正确的高度的网页内容。目前,我在委托方法 scrollViewDidScroll:中使用了它。

CGFloat contentHeight = scrollView.contentSize.height - CGRectGetHeight(scrollView.frame);

在 iOS 9.3模拟器/设备中验证,祝你好运!

编辑:

背景: html 内容由我的字符串变量和 HTTP 内容模板计算,由方法 loadHTMLString:baseURL:加载,没有注册的 JS 脚本。

在 Xcode 8和 iOS 10中确定 web 视图的高度

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
CGFloat height = [[webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight"] floatValue];
NSLog(@"Webview height is:: %f", height);
}

OR for Swift

 func webViewDidFinishLoad(aWebView:UIWebView){
let height: Float = (aWebView.stringByEvaluatingJavaScriptFromString("document.body.scrollHeight")?.toFloat())!
print("Webview height is::\(height)")
}

对于 iOS10,我得到的是 document.height的0(零)值,因此 document.body.scrollHeight是在 Webview 中获取文档高度的解决方案。这个问题也可以为 width解决。

Xcode8 Swift 3.1:

  1. 首先设置 webView 高度 0
  2. 代表:

Let height = webView.scrollView.contentSize.height

如果没有第1步,那么如果 webview.height > real contentHeight,第2步将返回 webview.height 但不返回 contentsize.height。