0

I have a custom WkWebView where I set the height of the view to the size of the html content.

This works great when I initialize it, but the problem comes when I change the source of the WkWebView to a shorter html.

I've already encountered this problem in the android version of my app and I fixed that by setting the HeightRequest to 0 before EvaluateJavaScriptAsync. In that way the view will always get bigger.

I tried the same thing with iOS but it keeps the highest content I had .

So for example :

If I set the source of the WkWebView to a html file that is 200px height and change it to a html file that is 1000px, it works fine and I can see all the content. BUT, if I try to go back to my 200px html file, I get a 800px blank space underneath and keep the 1000px height.

The goal is to always have the height of the WKWebView to adapt to the height of its content.

Here is the current version of the custom render

namespace MyNamespace.iOS.CustomControl
{
    public class AutoHeightWebViewRenderer : WkWebViewRenderer
    {
        protected override void OnElementChanged(VisualElementChangedEventArgs e)
        {
            try
            {
                base.OnElementChanged(e);

                if (NativeView != null)
                {
                    var webView = (WKWebView)NativeView;
                    NavigationDelegate = new ExtendedWKWebViewDelegate(this);
                }
            }
            catch (Exception ex)
            {
                //Log the Exception  
            }   
        }
    }


    class ExtendedWKWebViewDelegate : WKNavigationDelegate
    {

        AutoHeightWebViewRenderer webViewRenderer;

        public ExtendedWKWebViewDelegate(AutoHeightWebViewRenderer _webViewRenderer = null)
        {
            webViewRenderer = _webViewRenderer ?? new AutoHeightWebViewRenderer();
        }

        public override async void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
        {
            try
            {
                var _webView = webViewRenderer.Element as AutoHeightWebView;
                if (_webView != null)
                {
                    _webView.HeightRequest = 0d;
                    await Task.Delay(300);
                    var result = await _webView.EvaluateJavaScriptAsync("(function(){return document.body.scrollHeight;})()");
                    _webView.HeightRequest = Convert.ToDouble(result);
                }
            }
            catch (Exception ex)
            {
                //Log the Exception 
            }
        }

    }
}

EDIT 1 : To be clearer, I'm able to change the height of the webview, but I dont know the height because it always returned the height of the largest html displayed so far. Nomatter if I use _webView.EvaluateJavaScriptAsync("(function(){return document.body.scrollHeight;})()") or webView.ScrollView.ContentSize.Height.

EDIT 2 : Here a little sample to help understand the problem. I got two buttons and my custom webview (initialize with a 40 HeightRequest empty html). The first button set the Source of the webview to a 70px long HTML. The second one set the Source to a 280px long HTML.

enter image description here

In this example, I click on the first button than the second one and finaly back on the first button again. You see the webview getting bigger on the first 2 clicks. But then then webview should get shrunk when I choose back the first html (passing from 280px to 70px) but it keeps the 280px long.

First button (70px long)
enter image description here

Second button (280px long)
enter image description here

Back to the first button (should be 70px long instead of 280px). enter image description here

The problem occured on both simulator and iOS device.

2 Answers2

1

You can change the Frame of webView to change the height.

public override async void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
{
    try
    {
        var _webView = webViewRenderer.Element as WebView;
        if (_webView != null)
        {

            webView.Frame = new CGRect(webView.Frame.Location, new CGSize(webView.Frame.Size.Width, 0));

            var a = webView.ScrollView.ContentSize.Height;
            var b = await _webView.EvaluateJavaScriptAsync("(function(){return document.body.scrollHeight;})()");

            Console.WriteLine(a);
            Console.WriteLine(b);

            CGRect tempRect = webView.Frame;

            // for test                    
            webView.Frame = new CGRect(tempRect.Location.X, tempRect.Location.Y, tempRect.Size.Width, float.Parse(b));

        }
    }
    catch (Exception ex)
    {
        //Log the Exception 
    }
}
nevermore
  • 15,432
  • 1
  • 12
  • 30
  • Hi Jack! Thank you for your response. I tried your solution but the problem remains the same. The `(double)webView.ScrollView.ContentSize.Height` always gives the highest value. I got the same result if I use `EvaluateJavaScriptAsync`. I'm able to change the height of the view with the `HeightRequest`. The problem is that I don't know how long the html is(what the height of it). The height value returned is the height of the largest html displayed in the webview since its creation. In short, I need a way to get the current height of the current html, not the biggest one displayed so far. Thank – Christopher St-Pierre May 11 '20 at 15:56
  • Did you add a break point there to see the value of webView.ScrollView.ContentSize.Height? The value did change on my side with long or short html content. – nevermore May 12 '20 at 03:00
  • Yes I did. I just tested my custom webview in a separate view with 2 buttons. 1 that changes the source of the webview to a html 200px long and an other button that change the source to a html 100px long. The size is always 200px long. The `webView.ScrollView.ContentSize.Height` always return the biggest html used. This is happening on a real iPhone device. – Christopher St-Pierre May 12 '20 at 16:50
  • I just updated the post with an example and some screenshots. – Christopher St-Pierre May 12 '20 at 17:06
  • Can you have a try with my updated code to see if it works on your side? – nevermore May 13 '20 at 03:50
  • It works most of the time. I added a task.delay of 300 ms and seems to work all the time! – Christopher St-Pierre May 13 '20 at 17:39
0

I had the same problem and the CustomNavigationDelegate would also only show the same large size, which was the Device Display size.

I found that I had set this on the init part of the XAML and code behind, which somehow overrides the later content-based calculation.

See my fix here: https://stackoverflow.com/a/62409859/3443564

Kerry
  • 101
  • 1
  • 7