Làm cách nào để xác định kích thước nội dung của UIWebView?


116

Tôi có một UIWebViewvới nội dung (trang đơn) khác. Tôi muốn tìm hiểu CGSizenội dung để thay đổi kích thước chế độ xem dành cho cha mẹ của tôi một cách thích hợp. Điều hiển nhiên là -sizeThatFits:không may chỉ trả về kích thước khung hình hiện tại của webView.


1
Tôi không chắc, nhưng có lẽ câu trả lời cho câu hỏi này sẽ hữu ích: stackoverflow.com/questions/745160/…
Greg

Câu trả lời:


250

Hóa ra suy đoán đầu tiên của tôi khi sử dụng -sizeThatFits:không hoàn toàn sai. Nó dường như hoạt động, nhưng chỉ khi khung của webView được đặt ở kích thước tối thiểu trước khi gửi -sizeThatFits:. Sau đó chúng tôi có thể sửa lại kích thước khung sai bằng kích thước vừa vặn. Điều này nghe có vẻ kinh khủng nhưng thực ra nó không tệ như vậy. Vì chúng tôi thực hiện thay đổi cả hai khung ngay sau nhau, nên chế độ xem không được cập nhật và không nhấp nháy.

Tất nhiên, chúng tôi phải đợi cho đến khi nội dung được tải xong, vì vậy chúng tôi đặt mã vào -webViewDidFinishLoad:phương thức ủy nhiệm.

Ob-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

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
}

Tôi nên chỉ ra rằng có một cách tiếp cận khác (cảm ơn @GregInYEG) bằng cách sử dụng JavaScript. Không chắc chắn giải pháp nào hoạt động tốt hơn.

Trong số hai giải pháp hacky, tôi thích giải pháp này hơn.


Tôi đã thử cách tiếp cận của bạn. Vấn đề tôi gặp phải là không có cuộn. Bất kỳ đề xuất?
thử nghiệm

1
Tôi phát hiện ra rằng sự cố với việc cuộn xảy ra do chiều cao của chế độ xem web đã thay đổi. Tôi có phải thay đổi kích thước UIView không?
thử nghiệm

Không chắc chắn vấn đề của bạn là gì. Có lẽ bạn có thể đăng một câu hỏi mới với một số mã hoặc mô tả?
Ortwin Gentz

@testing, @Bogatyr: Đúng vậy, nếu của bạn UIWebViewcao hơn chế độ xem của mẹ bạn, bạn cần phải nhúng nó vào a UIScrollView.
Ortwin Gentz

7
nếu tôi cung cấp cho webView.scalesPageToFit = YES; thì chỉ có giải pháp này hoạt động..cảm ơn giải pháp ..
SP

92

Tôi có một giải pháp khác hoạt động tuyệt vời.

Một mặt, cách tiếp cận và giải pháp của Ortwin chỉ hoạt động với iOS 6.0 trở lên, nhưng không hoạt động chính xác trên iOS 5.0, 5.1 và 5.1.1, mặt khác có điều gì đó mà tôi không thích và không thể hiểu được với cách tiếp cận của Ortwin, đó là việc sử dụng phương thức [webView sizeThatFits:CGSizeZero]với tham số CGSizeZero: Nếu bạn đọc tài liệu Chính thức của Apple về phương thức này và tham số của nó, nó sẽ nói rõ ràng:

Việc triển khai mặc định của phương thức này trả về phần kích thước của hình chữ nhật giới hạn của khung nhìn. Các lớp con có thể ghi đè phương thức này để trả về giá trị tùy chỉnh dựa trên bố cục mong muốn của bất kỳ lượt xem con nào. Ví dụ: một đối tượng UISwitch trả về giá trị kích thước cố định đại diện cho kích thước chuẩn của chế độ xem chuyển đổi và đối tượng UIImageView trả về kích thước của hình ảnh mà nó hiện đang hiển thị.

Ý của tôi là giống như anh ấy đã xem qua giải pháp của mình mà không có bất kỳ logic nào, bởi vì đọc tài liệu, tham số được truyền đến [webView sizeThatFits: ...]ít nhất phải có mong muốn width. Với giải pháp của mình, chiều rộng mong muốn được đặt thành webViewkhung của trước khi gọi sizeThatFitsvới một CGSizeZerotham số. Vì vậy, tôi duy trì giải pháp này đang hoạt động trên iOS 6 một cách "tình cờ".

Tôi đã tưởng tượng ra một cách tiếp cận hợp lý hơn, có lợi thế là hoạt động cho iOS 5.0 trở lên ... Và cả trong các tình huống phức tạp khi có nhiều hơn một webView (Với thuộc tính của nó webView.scrollView.scrollEnabled = NOđược nhúng trong a scrollView.

Đây là mã của tôi để buộc Bố cục của webViewtheo mong muốn widthvà lấy lại bộ tương ứng heightvề webViewchính nó:

Ob-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

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;
}

Lưu ý rằng trong ví dụ của tôi, cái webViewđược nhúng trong một tùy chỉnh scrollViewcó cái khác webViews... Tất cả những cái này webViewsđều có cái khác webView.scrollView.scrollEnabled = NO, và đoạn mã cuối cùng tôi phải thêm là tính toán heightgiá contentSizetrị của scrollViewviệc nhúng tùy chỉnh của tôi webViews, nhưng nó rất dễ dàng như tổng hợp của tôi webView's frame.size.heighttính toán với các trick mô tả ở trên ...


3
Giải pháp này thanh lịch hơn nhiều so với giải pháp được chấp nhận và nó không dựa vào hành vi không có tài liệu và không xác định. Nó ít hack hơn nhiều so với việc chèn javascript. Tôi ước tôi có thể ủng hộ điều này 64 lần.
bugloaf,

Đó chính xác là những gì tôi đã cố gắng giải thích. Cảm ơn bạn. Và nó có lợi thế lớn là nó dường như hoạt động trên tất cả các phiên bản iOS.
Hiện tượng

Giải pháp thực sự có vẻ đẹp, nhưng hãy cẩn thận nếu bạn vẫn đang nhắm mục tiêu iOS 4.x. -[UIWebView scrollView]chỉ có sẵn trong iOS 5.0 trở lên.
Ortwin Gentz ​​29/11/12

2
Tôi đã thử cách này và một giải pháp tương tự và hóa ra là cách này chỉ hoạt động, nếu chế độ xem web hoặc chế độ xem mà nó được nhúng vào, đã xuất hiện trên màn hình. Nếu bạn thử điều này trước khi thêm chế độ xem web vào hệ thống phân cấp chế độ xem, kết quả sẽ là chiều cao kích thước thủ công.
k1th

1
giải pháp khác không hoạt động mỗi lần, không biết tại sao, nhưng điều này thực hiện mẹo !!! điều này sẽ được gắn thẻ một câu trả lời đúng.
Vassily

37

Đang trả lời câu hỏi này vì tôi thấy câu trả lời của Ortwin chỉ phù hợp nhất với thời gian ...

Các webViewDidFinishLoadphương pháp có thể được gọi nhiều hơn một lần, và giá trị đầu tiên được trả về bởi sizeThatFitschỉ một số phần của những gì kích thước cuối cùng nên. Sau đó, vì bất cứ lý do gì mà lệnh gọi tiếp theo sizeThatFitskhi webViewDidFinishLoadkích hoạt lại sẽ trả về không chính xác giá trị giống như trước đó! Điều này sẽ xảy ra ngẫu nhiên cho cùng một nội dung như thể đó là một số loại vấn đề đồng thời. Có thể hành vi này đã thay đổi theo thời gian, bởi vì tôi đang xây dựng cho iOS 5 và cũng nhận thấy rằng nó sizeToFithoạt động theo cách tương tự (mặc dù trước đây điều này không?)

Tôi đã quyết định giải pháp đơn giản này:

- (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):

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
    }
}

Cập nhật: Tôi nhận thấy như đã đề cập trong các nhận xét, điều này dường như không bắt được trường hợp nội dung bị thu hẹp. Không chắc liệu nó có đúng với tất cả nội dung và phiên bản hệ điều hành hay không, hãy thử.


Bạn đã đưa hình ảnh hoặc các nội dung khác vào trang web của mình chưa? Điều đó có thể gây ra kích hoạt bổ sung của đại biểu. Ngoài ra, giải pháp của bạn chỉ hoạt động khi bạn không đưa ra webView.scalesPageToFit = YESnhư @Sijo đã đề cập trong nhận xét ở trên.
Ortwin Gentz

Vâng, đó có thể là nó - giải pháp của bạn hoạt động tốt trong các trường hợp chỉ có văn bản của tôi. Nhưng sizeThatFits không đưa ra đúng giá trị trong lần gọi đại biểu cuối cùng là một vấn đề ... rất lạ. Làm thế nào chúng ta có thể biết cuộc gọi nào sẽ là cuộc gọi cuối cùng để chúng ta không cố gắng sử dụng sizeThatFits cho đến lúc đó?
Kieran Harper

Nếu trong lần gọi đại biểu đầu tiên bạn nhận được kết quả phù hợp từ sizeThatFits, bạn không thể bỏ qua bất kỳ lệnh gọi nào sau đây?
Ortwin Gentz

Đúng nhưng ngược lại :) Cuộc gọi đại biểu đầu tiên có thể dẫn đến sai sizeThatFits, sau đó giá trị đó sẽ bị loại bỏ. Có vẻ như một trong hai sizeThatFits đang sửa đổi bằng cách nào đó chế độ xem web để bất kỳ lệnh gọi sizeThatFits nào khác đều cho kết quả tương tự hoặc đó là thiết lập của khung trong khi nó vẫn đang tải đang thực hiện điều đó.
Kieran Harper

1
Giải pháp này cũng không hoạt động chính xác. Khi bạn đặt trang lớn hơn, rồi đặt trang nhỏ hơn, bạn sẽ luôn nhận được trả về giá trị lớn hơn. Có vẻ như nó "lười biếng" mở rộng các lượt xem nội bộ. Bất kỳ giải pháp thay thế cho điều này?
Legoless

23

Trong Xcode 8 và iOS 10 để xác định chiều cao của chế độ xem web. bạn có thể tăng chiều cao bằng cách sử dụng

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

HOẶC cho Swift

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

1
Cố gắng tìm cách sử dụng kích thước nội dung webview .. myWebView.scrollView.contentSize.height
Hardik Thakkar

8

Một giải pháp đơn giản là chỉ sử dụng webView.scrollView.contentSizenhưng tôi không biết liệu điều này có hoạt động với JavaScript hay không. Nếu không có JavaScript được sử dụng, điều này chắc chắn hoạt động:

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

3

AFAIK bạn có thể sử dụng [webView sizeThatFits:CGSizeZero]để tìm ra kích thước nội dung của nó.


3

Thật là lạ!

Tôi đã thử nghiệm các giải pháp cả hai sizeThatFits:[webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight"]đang không làm việc cho tôi.

Tuy nhiên, tôi đã tìm thấy một cách dễ dàng thú vị để có được chiều cao phù hợp của nội dung trang web. Hiện tại, tôi đã sử dụng nó trong phương pháp ủy nhiệm của mình scrollViewDidScroll:.

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

Đã được xác minh trong Trình mô phỏng / Thiết bị iOS 9.3, chúc bạn may mắn!

BIÊN TẬP:

Thông tin cơ bản: Nội dung html được tính toán bởi biến chuỗi của tôi và mẫu nội dung HTTP, được tải theo phương thức loadHTMLString:baseURL:, không có tập lệnh JS nào được đăng ký ở đó.


3

Đối với iOS10, tôi đã nhận được 0 giá trị (zero) của document.heightquá document.body.scrollHeightlà giải pháp để có được chiều cao của tài liệu Webview. Vấn đề cũng có thể được giải quyết cho width.


2

Tôi đang sử dụng chế độ UIWebViewxem không phải là chế độ xem phụ (và do đó không phải là một phần của phân cấp cửa sổ) để xác định kích thước của nội dung HTML UITableViewCells. Tôi thấy rằng phần bị ngắt kết nối UIWebViewkhông báo cáo kích thước của nó đúng với -[UIWebView sizeThatFits:]. Ngoài ra, như đã đề cập trong https://stackoverflow.com/a/3937599/9636 , bạn phải thiết lập UIWebView'sframe height thành 1 để có được chiều cao thích hợp.

Nếu UIWebViewchiều cao của quá lớn (tức là bạn đã đặt nó thành 1000, nhưng kích thước nội dung HTML chỉ là 500):

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

Tất cả đều trả heightvề 1000.

Để giải quyết vấn đề của tôi trong trường hợp này, tôi đã sử dụng https://stackoverflow.com/a/11770883/9636 , mà tôi đã bình chọn một cách nghiêm túc. Tuy nhiên, tôi chỉ sử dụng giải pháp này khi của tôi UIWebView.frame.widthgiống với -[UIWebView sizeThatFits:] width.


1
Tôi đã thử tất cả các ví dụ trong chuỗi này và sử dụng "document.height" để truy xuất chiều cao của tài liệu là đáng tin cậy nhất. HTML bổ sung có thể được thêm bên ngoài vùng chứa (chẳng hạn như DIV), điều này làm cho getElementById (...) không đáng tin cậy trong tất cả các thử nghiệm tôi đã thực hiện. document.height hoạt động tốt.
John Rogers

2

Không có gợi ý nào ở đây giúp tôi giải quyết tình huống của mình, nhưng tôi đã đọc được điều gì đó đã cho tôi câu trả lời. Tôi có một ViewController với một bộ điều khiển UI cố định, theo sau là UIWebView. Tôi muốn toàn bộ trang cuộn như thể các điều khiển giao diện người dùng được kết nối với nội dung HTML, vì vậy tôi tắt tính năng cuộn trên UIWebView và sau đó phải đặt chính xác kích thước nội dung của chế độ xem cuộn chính.

Mẹo quan trọng hóa ra là UIWebView không báo cáo kích thước của nó một cách chính xác cho đến khi được hiển thị trên màn hình. Vì vậy, khi tôi tải nội dung, tôi đặt kích thước nội dung bằng chiều cao màn hình có sẵn. Sau đó, trong viewDidAppear, tôi cập nhật kích thước nội dung của scrollview thành giá trị chính xác. Điều này phù hợp với tôi vì tôi đang gọi loadHTMLString trên nội dung cục bộ. Nếu bạn đang sử dụng loadRequest, bạn cũng có thể cần cập nhật contentSize trong webViewDidFinishLoad, tùy thuộc vào tốc độ truy xuất html.

Không có nhấp nháy, bởi vì chỉ phần không nhìn thấy của chế độ xem cuộn được thay đổi.


Tôi sợ rằng đó là điều may mắn khi chế độ xem web tải đủ nhanh để nó hoàn thành tại viewDidAppear. Có lẽ điều này chỉ hoạt động nếu chế độ xem xuất hiện hoạt ảnh để hoạt ảnh đủ dài để tải nội dung. Tôi muốn dựa vào webViewDidFinishLoadnhư một cách tiếp cận an toàn.
Ortwin Gentz

2

Nếu HTML của bạn chứa nhiều nội dung HTML như iframe (tức là facebook-, twitter, instagram-embeds) thì giải pháp thực sự khó hơn nhiều, trước tiên hãy kết thúc HTML của bạn:

[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>"];

Sau đó, thêm xử lý x-update- webview -height -scheme vào shouldStartLoadWithRequest của bạn :

    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;
    }
    ...

Và cuối cùng thêm mã sau vào bên trong layoutSubviews của bạn :

    ...
    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.
    ...

PS Bạn có thể triển khai tính năng trì hoãn thời gian (SetTimeout và setInterval) bên trong ObjectiveC / Swift-code của mình - tùy thuộc vào bạn.

PSS Thông tin quan trọng về UIWebView và Facebook Embeds: Bài đăng trên Facebook được nhúng không hiển thị chính xác trong UIWebView


Hay đấy! Bạn có thể mô tả phần script làm gì và nó có thể được tinh chỉnh như thế nào nếu cần? Có vẻ như nó sẽ kiểm tra chiều cao theo định kỳ, nhưng có nhiều khoảng thời gian liên quan?
Kieran Harper

@KieranHarper, bạn cần được mô tả chính xác điều gì?
MingalevME

2

Khi sử dụng chế độ xem web làm chế độ xem phụ ở đâu đó trong chế độ xem cuộn, bạn có thể đặt giới hạn chiều cao thành một số giá trị không đổi và sau đó tạo lối ra từ đó và sử dụng nó như:

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

1

Tôi cũng gặp khó khăn về vấn đề này, sau đó tôi nhận ra rằng nếu tôi muốn tính chiều cao động của webView, tôi cần phải cho biết chiều rộng của webView trước, vì vậy tôi thêm một dòng trước js và kết quả là tôi có thể nhận được chiều cao thực tế rất chính xác.

Mã đơn giản như sau:

-(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;

    //......
}

1

Xcode8 swift3.1:

  1. Đặt chiều cao webView thành 0 trước tiên.
  2. Trong webViewDidFinishLoadủy quyền:

let height = webView.scrollView.contentSize.height

Nếu không có bước 1, nếu webview.height> nội dung thực tế, thì bước 2 sẽ trả về webview.height nhưng không trả về contentize.height.


Đây là lý do tại sao tôi không thể nhận được khung hình yêu cầu trong trường hợp của mình .. đã
ủng hộ

0

Cũng trong iOS 7 để hoạt động bình thường của tất cả các phương pháp đã đề cập, hãy thêm điều này vào viewDidLoadphương thức bộ điều khiển chế độ xem của bạn :

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

Nếu không thì không có phương pháp nào hoạt động như bình thường.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.