Search code examples
xamarinxamarin.formsxamarin.ioswkwebviewcustom-renderer

Xamarin Forms WKWebView size doesn't shrink


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.


Solution

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