ios 如何确定 UIWebView 的内容大小?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3936041/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to determine the content size of a UIWebView?
提问by Ortwin Gentz
I have a UIWebView
with different (single page) content. I'd like to find out the CGSize
of the content to resize my parent views appropriately. The obvious -sizeThatFits:
unfortunately just returns the current frame size of the webView.
我有一个UIWebView
不同的(单页)内容。我想找出CGSize
内容以适当调整父视图的大小。-sizeThatFits:
不幸的是,显而易见的只是返回 webView 的当前帧大小。
回答by Ortwin Gentz
It turned out that my first guess using -sizeThatFits:
was not completely wrong. It seems to work, but only if the frame of the webView is set to a minimal size prior to sending -sizeThatFits:
. After that we can correct the wrong frame size by the fitting size. This sounds terrible but it's actually not that bad. Since we do both frame changes right after each other, the view isn't updated and doesn't flicker.
事实证明,我的第一个猜测使用-sizeThatFits:
并不是完全错误的。它似乎有效,但前提是在发送之前将 webView 的框架设置为最小尺寸-sizeThatFits:
。之后,我们可以通过拟合尺寸来纠正错误的框架尺寸。这听起来很可怕,但实际上并没有那么糟糕。由于我们在彼此之后立即进行两个框架更改,因此视图不会更新并且不会闪烁。
Of course, we have to wait until the content has been loaded, so we put the code into the -webViewDidFinishLoad:
delegate method.
当然,我们要等到内容加载完毕,所以我们把代码放到了-webViewDidFinishLoad:
委托方法中。
Obj-C
对象-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);
}
Swift 4.x
斯威夫特 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 another approach(thanks @GregInYEG) using JavaScript. Not sure which solution performs better.
我应该指出还有另一种使用 JavaScript 的方法(感谢 @GregInYEG)。不确定哪种解决方案性能更好。
Of two hacky solutions I like this one better.
在两个 hacky 解决方案中,我更喜欢这个。
回答by Phenomena
I have another solution that works great.
我有另一个效果很好的解决方案。
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 :
一方面,Ortwin 的方法和解决方案仅适用于 iOS 6.0 及更高版本,但无法在 iOS 5.0、5.1 和 5.1.1 上正常工作,另一方面有一些我不喜欢和无法理解的东西使用 Ortwin 的方法,它是使用[webView sizeThatFits:CGSizeZero]
带有参数的方法CGSizeZero
:如果您阅读有关此方法及其参数的 Apple 官方文档,它会清楚地说明:
The default implementation of this method returns the size portion of the view's bounds rectangle. Subclasses can override this method to return a custom value based on the desired layout of any subviews. For example, a UISwitch object returns a fixed size value that represents the standard size of a switch view, and a UIImageView object returns the size of the image it is currently displaying.
此方法的默认实现返回视图边界矩形的大小部分。子类可以覆盖此方法以根据任何子视图的所需布局返回自定义值。例如,UISwitch 对象返回一个固定大小值,表示切换视图的标准大小,而 UIImageView 对象返回它当前显示的图像的大小。
What I mean is that it's like he came across his solution without any logic, because reading the documentation, the parameter passed to [webView sizeThatFits: ...]
should at least have the desired width
. With his solution, the desired width is set to the webView
's frame before calling sizeThatFits
with a CGSizeZero
parameter. So I maintain this solution is working on iOS 6 by "chance".
我的意思是,这就像他在没有任何逻辑的情况下遇到了他的解决方案,因为阅读文档,传递给的参数[webView sizeThatFits: ...]
至少应该具有所需的width
. 使用他的解决方案,在使用参数webView
调用之前将所需的宽度设置为的框架。所以我认为这个解决方案是“偶然”在 iOS 6 上运行的。sizeThatFits
CGSizeZero
I imagined a more rational approach, which has the advantage of working for iOS 5.0 and later... And also in complex situations where more than one webView (With its property webView.scrollView.scrollEnabled = NO
is embedded in a scrollView
.
我想象了一种更合理的方法,它具有适用于 iOS 5.0 及更高版本的优势......而且在多个 webView(其属性webView.scrollView.scrollEnabled = NO
嵌入在scrollView
.
Here is my code to force the Layout of the webView
to the desired width
and get the corresponding height
set back to the webView
itself:
这是我的代码,用于强制将 Layout 设置webView
为所需的width
并将相应的height
设置返回到它webView
本身:
Obj-C
对象-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.
}
Swift 4.x
斯威夫特 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;
}
Note that in my example, the webView
was embedded in a custom scrollView
having other webViews
... All these webViews
had their webView.scrollView.scrollEnabled = NO
, and the last piece of code I had to add was the calculation of the height
of the contentSize
of my custom scrollView
embedding these webViews
, but it was as easy as summing my webView
's frame.size.height
computed with the trick described above...
请注意,在我的例子中,webView
被嵌入到定制scrollView
具有其它webViews
......所有这些webViews
有他们webView.scrollView.scrollEnabled = NO
,并且代码的最后一块,我不得不添加了的计算height
的contentSize
我的自定义的scrollView
嵌入这些webViews
,但它是那么容易用上面描述的技巧计算mywebView
的总和frame.size.height
......
回答by Kieran Harper
Resurrecting this question because I found Ortwin's answer to only work MOST of the time...
重新提出这个问题是因为我发现 Ortwin 的答案只能在大多数情况下工作......
The webViewDidFinishLoad
method may be called more than once, and the first value returned by sizeThatFits
is only some portion of what the final size should be. Then for whatever reason the next call to sizeThatFits
when webViewDidFinishLoad
fires again will incorrectly return the same value it did before! This will happen randomly for the same content as if it's some kind of concurrency problem. Maybe this behaviour has changed over time, because I'm building for iOS 5 and have also found that sizeToFit
works in much the same way (although previously this didn't?)
该webViewDidFinishLoad
方法可能被多次调用,并且返回的第一个值sizeThatFits
只是最终大小的一部分。然后无论出于何种原因,下一次调用sizeThatFits
whenwebViewDidFinishLoad
再次触发将错误地返回与之前相同的值!对于相同的内容,这将随机发生,就好像它是某种并发问题。也许这种行为随着时间的推移发生了变化,因为我正在为 iOS 5 构建并且也发现它的sizeToFit
工作方式大致相同(尽管以前没有?)
I have settled on this simple solution:
我已经确定了这个简单的解决方案:
- (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;
}
Swift (2.2):
斯威夫特(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
}
}
Update: I have found as mentioned in the comments this doesn't seem to catch the case where the content has shrunk. Not sure if it's true for all content and OS version, give it a try.
更新:我发现如评论中所述,这似乎没有抓住内容缩小的情况。不确定是否适用于所有内容和操作系统版本,请尝试一下。
回答by Hardik Thakkar
In Xcode 8 and iOS 10 to determine the height of a web view. you can get height using
在 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
或 Swift
func webViewDidFinishLoad(aWebView:UIWebView){
let height: Float = (aWebView.stringByEvaluatingJavaScriptFromString("document.body.scrollHeight")?.toFloat())!
print("Webview height is::\(height)")
}
回答by OemerA
A simple solution would be to just use webView.scrollView.contentSize
but I don't know if this works with JavaScript. If there is no JavaScript used this works for sure:
一个简单的解决方案是使用,webView.scrollView.contentSize
但我不知道这是否适用于 JavaScript。如果没有使用 JavaScript,这肯定有效:
- (void)webViewDidFinishLoad:(UIWebView *)aWebView {
CGSize contentSize = aWebView.scrollView.contentSize;
NSLog(@"webView contentSize: %@", NSStringFromCGSize(contentSize));
}
回答by Rengers
AFAIK you can use [webView sizeThatFits:CGSizeZero]
to figure out it's content size.
AFAIK 您可以[webView sizeThatFits:CGSizeZero]
用来确定它的内容大小。
回答by iAnkit
For iOS10, I was getting 0 (zero) value of document.height
so document.body.scrollHeight
is the solution to get height of document in Webview. The issue can be resolved also for width
.
对于iOS10,我得到0(零)值document.height
所以document.body.scrollHeight
是在Webview中获取文档高度的解决方案。该问题也可以为width
.
回答by Itachi
This's weird!
这很奇怪!
I tested the solutions both sizeThatFits:
and [webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight"]
are NOTworking for me.
我测试的解决方案,既sizeThatFits:
和[webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight"]
被不为我工作。
However, I found an interesting easy way to get the right height of webpage content. Currently, I used it in my delegate method scrollViewDidScroll:
.
但是,我发现了一种有趣的简单方法来获得网页内容的正确高度。目前,我在我的委托方法中使用了它scrollViewDidScroll:
。
CGFloat contentHeight = scrollView.contentSize.height - CGRectGetHeight(scrollView.frame);
Verified in iOS 9.3 Simulator/Device, good luck!
在 iOS 9.3 模拟器/设备中验证,祝你好运!
EDIT:
编辑:
Background: The html content is calculated by my string variable and HTTP content template, loaded by method loadHTMLString:baseURL:
, no registered JS scripts there.
背景:html内容由我的字符串变量和HTTP内容模板计算,方法加载loadHTMLString:baseURL:
,没有注册JS脚本。
回答by Eli Burke
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.
这里的建议都没有帮助我解决我的情况,但我读到了一些确实给了我答案的东西。我有一个带有一组固定 UI 控件的 ViewController,后跟一个 UIWebView。我希望整个页面都像 UI 控件连接到 HTML 内容一样滚动,因此我禁用了 UIWebView 上的滚动,然后必须正确设置父滚动视图的内容大小。
The important tip turned out to be that UIWebView does not report its size correctly until rendered to the screen. So when I load the content I set the content size to the available screen height. Then, in viewDidAppear I update the scrollview's content size to the correct value. This worked for me because I am calling loadHTMLString on local content. If you are using loadRequest you may need to update the contentSize in webViewDidFinishLoad also, depending on how quickly the html is retrieved.
重要的提示是 UIWebView 在呈现到屏幕之前不会正确报告其大小。因此,当我加载内容时,我将内容大小设置为可用的屏幕高度。然后,在viewDidAppear我更新了滚动的内容大小为正确的值。这对我有用,因为我在本地内容上调用 loadHTMLString 。如果您正在使用 loadRequest,您可能还需要更新 webViewDidFinishLoad 中的 contentSize,具体取决于检索 html 的速度。
There is no flickering, because only the invisible part of the scroll view is changed.
没有闪烁,因为只有滚动视图的无形部分改变。
回答by MingalevME
If your HTML contains heavyHTML-contents like iframe's (i.e. facebook-, twitter, instagram-embeds) the real solution is much more difficult, first wrap your HTML:
如果您的 HTML 包含像 iframe 的大量HTML 内容(即 facebook-、twitter、instagram-embeds),真正的解决方案要困难得多,首先包装您的 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>"];
Then add handling x-update-webview-height-scheme into your shouldStartLoadWithRequest:
然后将处理x-update-webview-height-scheme 添加到您的shouldStartLoadWithRequest 中:
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;
}
...
And finally add the following code inside your layoutSubviews:
最后在layoutSubviews 中添加以下代码:
...
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.
...
P.S. You can implement time delaying (SetTimeout and setInterval) inside your ObjectiveC/Swift-code - it's up to you.
PS 你可以在你的 ObjectiveC/Swift 代码中实现时间延迟(SetTimeout 和 setInterval)——这取决于你。
P.S.S. Important info about UIWebView and Facebook Embeds: Embedded Facebook post does not shows properly in UIWebView
PSS 关于 UIWebView 和 Facebook 嵌入的重要信息:嵌入的 Facebook 帖子在 UIWebView 中无法正确显示