簡體   English   中英

如何確定 WKWebView 的內容大小?

[英]How to determine the content size of a WKWebView?

在 iOS 8 及更高版本下運行時,我正在嘗試用 WKWebView 實例替換動態分配的 UIWebView 實例,但我找不到確定 WKWebView 內容大小的方法。

我的 web 視圖嵌入在更大的 UIScrollView 容器中,因此我需要確定 web 視圖的理想大小。 這將允許我修改其框架以顯示其所有 HTML 內容,而無需在 web 視圖中滾動,並且我將能夠為滾動視圖容器設置正確的高度(通過設置 scrollview.contentSize)。

我試過 sizeToFit 和 sizeThatFits 都沒有成功。 這是我創建 WKWebView 實例並將其添加到容器滾動視圖的代碼:

// self.view is a UIScrollView sized to something like 320.0 x 400.0.
CGRect wvFrame = CGRectMake(0, 0, self.view.frame.size.width, 100.0);
self.mWebView = [[[WKWebView alloc] initWithFrame:wvFrame] autorelease];
self.mWebView.navigationDelegate = self;
self.mWebView.scrollView.bounces = NO;
self.mWebView.scrollView.scrollEnabled = NO;

NSString *s = ... // Load s from a Core Data field.
[self.mWebView loadHTMLString:s baseURL:nil];

[self.view addSubview:self.mWebView];

這是一個實驗性的 didFinishNavigation 方法:

- (void)webView:(WKWebView *)aWebView
                             didFinishNavigation:(WKNavigation *)aNavigation
{
    CGRect wvFrame = aWebView.frame;
    NSLog(@"original wvFrame: %@\n", NSStringFromCGRect(wvFrame));
    [aWebView sizeToFit];
    NSLog(@"wvFrame after sizeToFit: %@\n", NSStringFromCGRect(wvFrame));
    wvFrame.size.height = 1.0;
    aWebView.frame = wvFrame;
    CGSize sz = [aWebView sizeThatFits:CGSizeZero];
    NSLog(@"sizeThatFits A: %@\n", NSStringFromCGSize(sz));
    sz = CGSizeMake(wvFrame.size.width, 0.0);
    sz = [aWebView sizeThatFits:sz];
    NSLog(@"sizeThatFits B: %@\n", NSStringFromCGSize(sz));
}

這是生成的 output:

2014-12-16 17:29:38.055 App[...] original wvFrame: {{0, 0}, {320, 100}}
2014-12-16 17:29:38.055 App[...] wvFrame after sizeToFit: {{0, 0}, {320, 100}}
2014-12-16 17:29:38.056 App[...] wvFrame after sizeThatFits A: {320, 1}
2014-12-16 17:29:38.056 App[...] wvFrame after sizeThatFits B: {320, 1}

sizeToFit 調用無效,並且 sizeThatFits 始終返回高度 1。

我想我閱讀了有關此主題的所有答案,而我所擁有的只是解決方案的一部分。 大部分時間我都花在嘗試實現@davew 描述的 KVO 方法,它偶爾會起作用,但大部分時間在 WKWebView 容器的內容下留下了一個空白。 我還實現了@David Beck 的建議,並將容器高度設為 0,從而避免了容器高度大於內容高度時出現問題的可能性。 盡管如此,我還是有偶爾的空白。 所以,對我來說,“contentSize”觀察者有很多缺陷。 我沒有太多的網絡技術經驗,所以我無法回答這個解決方案有什么問題,但我看到如果我只在控制台中打印高度但不做任何事情(例如,調整約束的大小),它跳到某個數字(例如 5000),然后跳到最高數字之前的數字(例如 2500 - 結果是正確的數字)。 如果我確實將高度約束設置為我從“contentSize”獲得的高度,它會將自己設置為它獲得的最高數字,並且永遠不會調整為正確的高度 - 這也是@David Beck 評論提到的。

經過大量實驗,我設法找到了適合我的解決方案:

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    self.webView.evaluateJavaScript("document.readyState", completionHandler: { (complete, error) in
        if complete != nil {
            self.webView.evaluateJavaScript("document.body.scrollHeight", completionHandler: { (height, error) in
                self.containerHeight.constant = height as! CGFloat
            })
        }

        })
}

當然,重要的是要正確設置約束,以便 scrollView 根據 containerHeight 約束調整大小。

事實證明,didFinish 導航方法永遠不會在我想要的時候被調用,但是設置了document.readyState步驟后,下一個( document.body.offsetHeight )會在正確的時刻被調用,並返回正確的高度數字。

您可以使用鍵值觀察(KVO)...

在您的視圖控制器中:

- (void)viewDidLoad {
    ...
    [self.webView.scrollView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:nil];
}


- (void)dealloc
{
    [self.webView.scrollView removeObserver:self forKeyPath:@"contentSize" context:nil];
}


- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    if (object == self.webView.scrollView && [keyPath isEqual:@"contentSize"]) {
        // we are here because the contentSize of the WebView's scrollview changed.

        UIScrollView *scrollView = self.webView.scrollView;
        NSLog(@"New contentSize: %f x %f", scrollView.contentSize.width, scrollView.contentSize.height);
    }
}

這將節省 JavaScript 的使用並讓您隨時了解所有更改。

我最近不得不自己處理這個問題。 最后,我使用了Chris McClenaghan 提出的解決方案的修改。

實際上,他的原始解決方案非常好,並且適用於大多數簡單的情況。 但是,它只適用於帶有文本的頁面。 它可能也適用於具有靜態高度的圖像的頁面。 但是,當您擁有使用max-heightmax-width屬性定義大小的圖像時,它肯定不起作用。

這是因為這些元素可以在頁面加載調整大小。 所以,實際上,在onLoad中返回的高度總是正確的。 但它只對那個特定的實例是正確的。 解決方法是監測body高度的變化並做出反應。

監控document.body的大小調整

var shouldListenToResizeNotification = false
lazy var webView:WKWebView = {
    //Javascript string
    let source = "window.onload=function () {window.webkit.messageHandlers.sizeNotification.postMessage({justLoaded:true,height: document.body.scrollHeight});};"
    let source2 = "document.body.addEventListener( 'resize', incrementCounter); function incrementCounter() {window.webkit.messageHandlers.sizeNotification.postMessage({height: document.body.scrollHeight});};"
    
    //UserScript object
    let script = WKUserScript(source: source, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
    
    let script2 = WKUserScript(source: source2, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
    
    //Content Controller object
    let controller = WKUserContentController()
    
    //Add script to controller
    controller.addUserScript(script)
    controller.addUserScript(script2)
    
    //Add message handler reference
    controller.add(self, name: "sizeNotification")
    
    //Create configuration
    let configuration = WKWebViewConfiguration()
    configuration.userContentController = controller
    
    return WKWebView(frame: CGRect.zero, configuration: configuration)
}()

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    guard let responseDict = message.body as? [String:Any],
    let height = responseDict["height"] as? Float else {return}
    if self.webViewHeightConstraint.constant != CGFloat(height) {
        if let _ = responseDict["justLoaded"] {
            print("just loaded")
            shouldListenToResizeNotification = true
            self.webViewHeightConstraint.constant = CGFloat(height)
        }
        else if shouldListenToResizeNotification {
            print("height is \(height)")
            self.webViewHeightConstraint.constant = CGFloat(height)
        }
        
    }
}

這個解決方案是迄今為止我能想到的最優雅的解決方案。 但是,您應該注意兩件事。

首先,在加載您的 URL 之前,您應該將shouldListenToResizeNotification設置為false 當加載的 URL 可以快速更改時,需要這種額外的邏輯。 發生這種情況時,出於某種原因,來自舊內容的通知可能會與來自新內容的通知重疊。 為了防止這種行為,我創建了這個變量。 它確保一旦我們開始加載新內容,我們將不再處理來自舊內容的通知,並且我們只會在加載新內容后恢復處理調整大小通知。

然而,最重要的是,您需要注意這一點:

如果您采用此解決方案,您需要考慮到,如果您將WKWebView的大小更改為通知報告的大小以外的任何內容 - 將再次觸發通知。

請注意這一點,因為它很容易進入無限循環。 例如,如果您決定通過使您的高度等於報告的高度 + 一些額外的填充來處理通知:

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        guard let responseDict = message.body as? [String:Float],
        let height = responseDict["height"] else {return}
        self.webViewHeightConstraint.constant = CGFloat(height+8)
    }

如您所見,因為我在報告的高度上增加了 8,所以完成此操作后,我的body大小會發生變化,並且會再次發布通知。

警惕這種情況,否則你應該沒事。

如果您發現此解決方案有任何問題,請告訴我 - 我自己依賴它,所以最好知道是否有一些我沒有發現的錯誤!

為我工作

extension TransactionDetailViewController: WKNavigationDelegate {
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
            self.webviewHeightConstraint.constant = webView.scrollView.contentSize.height
        }
    }
}

試試下面的。 無論您在哪里實例化 WKWebView 實例,都添加類似於以下內容的內容:

    //Javascript string
    NSString * source = @"window.webkit.messageHandlers.sizeNotification.postMessage({width: document.width, height: document.height});";

    //UserScript object
    WKUserScript * script = [[WKUserScript alloc] initWithSource:source injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];

    //Content Controller object
    WKUserContentController * controller = [[WKUserContentController alloc] init];

    //Add script to controller
    [controller addUserScript:script];

    //Add message handler reference
    [controller addScriptMessageHandler:self name:@"sizeNotification"];

    //Create configuration
    WKWebViewConfiguration * configuration = [[WKWebViewConfiguration alloc] init];

    //Add controller to configuration
    configuration.userContentController = controller;

    //Use whatever you require for WKWebView frame
    CGRect frame = CGRectMake(...?);

    //Create your WKWebView instance with the configuration
    WKWebView * webView = [[WKWebView alloc] initWithFrame:frame configuration:configuration];

    //Assign delegate if necessary
    webView.navigationDelegate = self;

    //Load html
    [webView loadHTMLString:@"some html ..." baseURL:[[NSBundle mainBundle] bundleURL]];

然后添加一個類似於以下的方法,該類遵循 WKScriptMessageHandler 協議來處理消息:

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    CGRect frame = message.webView.frame;
    frame.size.height = [[message.body valueForKey:@"height"] floatValue];
    message.webView.frame = frame;}

這對我有用。

如果您的文檔中有多個文本,則可能需要像這樣包裝 javascript 以確保所有內容都已加載:

@"window.onload=function () { window.webkit.messageHandlers.sizeNotification.postMessage({width: document.width, height: document.height});};"

注意:此解決方案不解決對文檔的持續更新。

您還可以通過 evaluateJavaScript 獲得 WKWebView 的內容高度。

- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    [webView evaluateJavaScript:@"Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight)"
              completionHandler:^(id _Nullable result, NSError * _Nullable error) {
                  if (!error) {
                      CGFloat height = [result floatValue];
                      // do with the height

                  }
              }];
}

您需要等待 webview 完成加載。 這是我使用的一個工作示例

WKWebView 內容加載函數永遠不會被調用

然后在 webview 完成加載后,您可以通過

func webView(webView: WKWebView!, didFinishNavigation navigation: WKNavigation!) {

   println(webView.scrollView.contentSize.height)

}

大多數答案都使用“document.body.offsetHeight”。

這隱藏了身體的最后一個對象。

我通過使用 KVO 觀察者來監聽 WKWebview “contentSize”中的變化,然后運行以下代碼,從而克服了這個問題:

self.webView.evaluateJavaScript(
    "(function() {var i = 1, result = 0; while(true){result = 
    document.body.children[document.body.children.length - i].offsetTop + 
    document.body.children[document.body.children.length - i].offsetHeight;
    if (result > 0) return result; i++}})()",
    completionHandler: { (height, error) in
        let height = height as! CGFloat
        self.webViewHeightConstraint.constant = height
    }
)

這不是最漂亮的代碼,但它對我有用。

我發現 hlung here 的答案,如下擴展 WKWebView 對我來說是最簡單和最有效的解決方案:

https://gist.github.com/pkuecuekyan/f70096218a6b969e0249427a7d324f91

他的評論如下:

“太好了!對我來說,我沒有設置 webView.frame,而是設置了 autolayout intrinsicContentSize。”

他的代碼如下:

import UIKit
import WebKit

class ArticleWebView: WKWebView {

  init(frame: CGRect) {
    let configuration = WKWebViewConfiguration()
    super.init(frame: frame, configuration: configuration)
    self.navigationDelegate = self
  }

  required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

  override var intrinsicContentSize: CGSize {
    return self.scrollView.contentSize
  }

}

extension ArticleWebView: WKNavigationDelegate {

  func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    webView.evaluateJavaScript("document.readyState", completionHandler: { (_, _) in
      webView.invalidateIntrinsicContentSize()
    })
  }

}

這是對@IvanMih 答案的輕微編輯。 對於那些在WKWebview結束時遇到大空白區域的人來說,這個解決方案對我來說效果很好:

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
  webView.evaluateJavaScript("document.readyState", completionHandler: { (complete, error) in

    if complete != nil {
      let height = webView.scrollView.contentSize
      print("height of webView is: \(height)")
    }
  })
}

所以基本上不是根據scrollHeight計算高度,而是使用webView.scrollView.contentSize計算高度。 我確信在某些情況下這會中斷,但我認為它對於靜態內容會做得很好,並且如果您在無需用戶滾動的情況下顯示所有內容。

經過大量實驗后,我設法找到了一個適合我的解決方案樣式為 HTML 並使用字體大小和高度

Swift 中的代碼

1-給你的 Webview 導航委托

  webView.navigationDelegate = self

2-在代表團擴展

extension yourclass : WKNavigationDelegate {
      func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        // Handel Dynamic Height For Webview Loads with HTML
       // Most important to reset webview height to any desired height i prefer 1 or 0  
        webView.frame.size.height = 1
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
        // here get height constant and assign new height in it 
            if let constraint = (webView.constraints.filter{$0.firstAttribute == .height}.first) {
                constraint.constant = webView.scrollView.contentSize.height
            }
 }

希望它也適用於你們

使用@Andriy 的答案和這個答案,我能夠在 WKWebView 中設置獲取 contentSize 的高度並更改它的高度。

這是完整的 swift 4 代碼:

    var neededConstraints: [NSLayoutConstraint] = []

    @IBOutlet weak var webViewContainer: UIView!
    @IBOutlet weak var webViewHeight: NSLayoutConstraint! {
        didSet {
            if oldValue != nil, oldValue.constant != webViewHeight.constant {
                view.layoutIfNeeded()
            }
        }
    }


   lazy var webView: WKWebView = {
        var source = """
var observeDOM = (function(){
    var MutationObserver = window.MutationObserver || window.WebKitMutationObserver,
        eventListenerSupported = window.addEventListener;

    return function(obj, callback){
        if( MutationObserver ){
            // define a new observer
            var obs = new MutationObserver(function(mutations, observer){
                if( mutations[0].addedNodes.length || mutations[0].removedNodes.length )
                    callback();
            });
            // have the observer observe foo for changes in children
            obs.observe( obj, { childList:true, subtree:true });
        }
        else if( eventListenerSupported ){
            obj.addEventListener('DOMNodeInserted', callback, false);
            obj.addEventListener('DOMNodeRemoved', callback, false);
        }
    };
})();

// Observe a specific DOM element:
observeDOM( document.body ,function(){
    window.webkit.messageHandlers.sizeNotification.postMessage({'scrollHeight': document.body.scrollHeight,'offsetHeight':document.body.offsetHeight,'clientHeight':document.body.clientHeight});
});

"""

        let script = WKUserScript(source: source, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
        let controller = WKUserContentController()
        controller.addUserScript(script)
        controller.add(self, name: "sizeNotification")
        let configuration = WKWebViewConfiguration()
        configuration.userContentController = controller
        let this = WKWebView(frame: .zero, configuration: configuration)
        webViewContainer.addSubview(this)
        this.translatesAutoresizingMaskIntoConstraints = false
        this.scrollView.isScrollEnabled = false
        // constraint for webview when added to it's superview
        neededConstraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|[web]|",
                                                            options: [],
                                                            metrics: nil,
                                                            views: ["web": this])
        neededConstraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|[web]|",
                                                            options: [],
                                                            metrics: nil,
                                                            views: ["web": this])
        return this
    }()


    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        _  = webView // to create constraints needed for webView
        NSLayoutConstraint.activate(neededConstraints)
        let url = URL(string: "https://www.awwwards.com/")!
        let request = URLRequest(url: url)
        webView.load(request)
    }

    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if let body = message.body as? Dictionary<String, CGFloat>,
            let scrollHeight = body["scrollHeight"],
            let offsetHeight = body["offsetHeight"],
            let clientHeight = body["clientHeight"] {
            webViewHeight.constant = scrollHeight
            print(scrollHeight, offsetHeight, clientHeight)
        }
    }

我在 UITableViewCell 中嘗試了 Javascript 版本,它運行良好。 但是,如果你想把它放在滾動視圖中。 我不知道為什么,高度可以更高,但不能更短。 但是,我在這里找到了 UIWebView 解決方案。 https://stackoverflow.com/a/48887971/5514452

它也適用於 WKWebView。 我認為問題是因為 WebView 需要重新布局,但不知何故它不會縮小,只能放大。 我們需要重置高度,它肯定會調整大小。

編輯:我在設置約束后重置了框架高度,因為有時由於將框架高度設置為 0,它無法工作。

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    self.webView.frame.size.height = 0
    self.webView.evaluateJavaScript("document.readyState", completionHandler: { (complete, error) in
        if complete != nil {
            self.webView.evaluateJavaScript("document.body.scrollHeight", completionHandler: { (height, error) in
                let webViewHeight = height as! CGFloat
                self.webViewHeightConstraint.constant = webViewHeight
                self.webView.frame.size.height = webViewHeight
            })
        }
    })
}

我已經嘗試過滾動視圖 KVO 並且我已經嘗試使用clientHeightoffsetHeight等評估文檔上的 javascript...

最終對我有用的是: document.body.scrollHeight 或者使用最頂層元素的scrollHeight ,例如容器div

我使用 KVO 監聽loading WKWebview 屬性的變化:

[webview addObserver: self forKeyPath: NSStringFromSelector(@selector(loading)) options: NSKeyValueObservingOptionNew context: nil];

接着:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if(object == self.webview && [keyPath isEqualToString: NSStringFromSelector(@selector(loading))]) {
        NSNumber *newValue = change[NSKeyValueChangeNewKey];
        if(![newValue boolValue]) {
            [self updateWebviewFrame];
        }
    }
}

updateWebviewFrame實現:

[self.webview evaluateJavaScript: @"document.body.scrollHeight" completionHandler: ^(id response, NSError *error) {
     CGRect frame = self.webview.frame;
     frame.size.height = [response floatValue];
     self.webview.frame = frame;
}];

也嘗試了不同的方法,最終得出了一個解決方案。 結果,我制作了一個自調整大小的 WKWebView,它使它的intrinsicContentSize適應其內容的大小。 所以你可以在Auto Layouts中使用它。 作為一個例子,我做了一個視圖,它可以幫助你在 iOS 應用程序上顯示數學公式: https ://github.com/Mazorati/SVLatexView

以下代碼對我來說非常適合 webkit 中的任何內容。 確保將以下委托添加到您的類:WKNavigationDelegate。

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
        self.bodyWebView.evaluateJavaScript("document.readyState", completionHandler: { (complete, error) in
            if complete != nil {
                self.bodyWebView.evaluateJavaScript("document.body.scrollHeight", completionHandler: { (height, error) in
                    let heightWebView = height as! CGFloat
                    //heightWebView is the height of the web view
                })
            }
        })
    }
}

調度很重要,因為這樣可以確保在加載 web 視圖結束時獲得的高度是正確的,這是因為 html 可能具有的元素類型。

我想為上述答案中未提及的特殊情況提供解決方案,如果您在 WKWebView 中使用自定義字體,可能會發生這種情況。

我嘗試了此處解釋的所有解決方案,以及其他 StackOverflow 問題中提到的許多其他解決方案。 沒有什么對我來說是 100% 正確的。 我總是遇到同樣的問題:返回的高度總是比 WkWebView 的實際高度小一點 我嘗試了 WKNavigationDelegate 的方式,並嘗試通過將 js 注入呈現的 HTML 中來監聽自生成的事件,但沒有成功,在所有情況下高度總是錯誤的。

我學到的第一件事是:在加載 html 並等待完成事件之前,必須將 webview 添加到布局中。 如果您嘗試以孤立的方式渲染 webview 而不將其添加到布局中,那么高度將非常錯誤。

奇怪的是,我發現在html渲染之后設置斷點,在調用高度評估方法之前,返回的高度是正確的。 測量哪個高度(scrollHeight 或 offsetheight)並不重要,兩者總是正確的。

這為我指明了正確的方向。 結論很明顯(雖然我花了很多天調試才意識到):收到didFinishNavigation事件后,或者如果你使用自定義js並監聽window.onload事件或類似事件,返回的高度幾乎是正確的但不完全是因為渲染還沒有完成。

正如這里所解釋的, Firefox、Chrome 和 Safari 在 font-face 應用於文檔之前觸發 DomContenLoaded 事件(也許,在 css 也應用於文檔之前?)。 就我而言,我使用的是嵌入在我的應用程序中的自定義字體,並以經典方式在 HTML 中引用:

 <style>
    @font-face {
        font-family: 'CustomFont';
        src: url('montserrat.ttf');
        format('truetype');
    }

    body{
        font-family: 'CustomFont';
        font-size: 12px;
    }

解決方案? 您必須收聽事件 document.fonts.ready,該事件發生在事件 window.onload等之后。 在 WkWebView 中加載的 html 中嵌入以下 js:

    document.fonts.ready.then(function() {
 window.webkit.messageHandlers.iosEventListener.postMessage('custom_event_fonts_ready');
  
});

然后在您的 iOS 應用程序中,使用

  self.webView.configuration.userContentController.add(self, name: "iosEventListener")

當收到

        public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
            if let body = message.body as? String {
                if (body == "custom_event_fonts_ready") {
                        self.evaluateBodyHeight()
    }
            }
        }

 private func evaluateBodyHeight() {
        self.webView.evaluateJavaScript("document.readyState", completionHandler: { (complete, error) in
            if complete != nil {
                self.webView.evaluateJavaScript("document.body.scrollHeight", completionHandler: { (height, error) in
                    let webViewHeight = height as! CGFloat
//Do something with the height.


                })

            }
        })

    }

我不確定,但我認為使用此解決方案,所有測量 Web 視圖高度的不同方法都將返回正確的方法。 經過將近一個月的調試和絕望,我不想測試它們

為我糟糕的英語道歉。

最好的方法是觀察 webView.scrollView 的contentSize屬性並相應地更新 webView 的高度約束

private var contentSizeObserver: NSKeyValueObservation?
    
contentSizeObserver = webView.scrollView.observe(\.contentSize, options: .new) { [weak self] _, change in
    guard let contentSize = change.newValue else { return }
    self?.csWebViewHeight?.update(offset: contentSize.height)
}

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    // Recalculate webView size
    csWebViewHeight?.update(offset: 0)
    webView.setNeedsLayout()
    webView.layoutIfNeeded()
}

列出的用於獲取內容高度的 JS 函數都沒有為我可靠地工作。 我發現始終有效的是找到 DOM 中的最后一個元素並明確獲取其 position:

    webView.evaluateJavaScript("document.body.lastChild.getBoundingClientRect().bottom + window.scrollY") { [weak self] (result, _) in
                guard let self = self,
                      let height = result as? CGFloat,
                        height > 0 else { return }
                
                self.heightConstraint?.constant = height
 }

您需要添加延遲(它對我有用),而不是上面的JS解決方案:

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
        print(size: webView.scrollView.contentSize)
    })
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM