Search code examples
xamarinxamarin.formswebviewxamarin.ioswkwebview

Implementing Custom Webview in Xamarin Forms


I'm new with Xamarin.Forms and I have implemented a custom web view renderer in Droid project.

The issue is when implementing the renderer in iOS project, it's like the Webview is initialized without loading the CSS and Javascript. Because it only display HTML page without any functionality.

After some research, I know we have to implement WKWebviewRenderer and we have to load the LoadFileUrl method in iOS but I still can't catch the url in the renderer.

Anyone have an idea please about how to implement the following Android Renderer code into iOS Renderer??

Custom renderer in Droid project:

[assembly: ExportRenderer(typeof(Xamarin.Forms.WebView), typeof(MyProject.Droid.WebViewRenderer))]
namespace MyProject.Droid
{
    public class WebViewRenderer : Xamarin.Forms.Platform.Android.WebViewRenderer
    {
        private bool isMyCustomWebview = false;
        public IWebViewController ElementController => Element;

        protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Xamarin.Forms.WebView> e)
        {
            base.OnElementChanged(e);
            if (e.NewElement.GetType() == typeof(MyCustomWebview))
            {
                Control.SetWebViewClient(new Callback(this));
                isMyCustomWebview = true;
            }
            else
            {
                Control.SetWebViewClient(new Callback(Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity));
                isMyCustomWebview = false;
            }
        }


        public class Callback : WebViewClient
        {
            Activity _context;
            public Callback(Activity _context)
            {
                this._context = _context;
            }

            WebViewRenderer _renderer;

            public Callback(WebViewRenderer renderer)
            {
                _renderer = renderer ?? throw new ArgumentNullException("Renderer");
            }

            public override void OnPageStarted(Android.Webkit.WebView view, string url, Android.Graphics.Bitmap favicon)
            {
                base.OnPageStarted(view, url, favicon);
                if (_renderer != null && _renderer.isMyCustomWebview)
                {
                    DependencyService.Get<ILoadingIndicator>().Show();
                    var args = new WebNavigatingEventArgs(WebNavigationEvent.NewPage, new UrlWebViewSource { Url = url }, url);
                    _renderer.ElementController.SendNavigating(args);
                }

            }
            public override void OnPageFinished(Android.Webkit.WebView view, string url)
            {
                base.OnPageFinished(view, url);
                if (_renderer != null && _renderer.isMyCustomWebview)
                {
                    DependencyService.Get<ILoadingIndicator>().Dismiss();
                    var source = new UrlWebViewSource { Url = url };
                    var args = new WebNavigatedEventArgs(WebNavigationEvent.NewPage, source, url, WebNavigationResult.Success);
                    _renderer.ElementController.SendNavigated(args);
                }
            }
        }
    }
}

UPDATE: Custom renderer in iOS project:

[assembly: ExportRenderer(typeof(Xamarin.Forms.WebView), typeof(MyProject.iOS.WebViewRenderer))]
namespace MyProject.iOS
{
    // Xamarin.Forms.Platform.iOS.WebViewRenderer
    public class WebViewRenderer : ViewRenderer<WebView, WKWebView>
    {
        WKWebView wkWebView;

        protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
        {
            base.OnElementChanged(e);
            if (Control != null) return;
            var config = new WKWebViewConfiguration();
            wkWebView = new WKWebView(Frame, config) { NavigationDelegate = new MyNavigationDelegate() };
            SetNativeControl(wkWebView);
        }
    }

    public class MyNavigationDelegate : WKNavigationDelegate
    {
        public override void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
        {

            //get url here
            var url = webView.Url;

            // webView.LoadFileUrl(url, url);

        }
    }
}

UPDATE:

Here's how I'm setting the source and base url for the webview in the portable project:

var result = await client.PostAsync("/embedded/pay", content);
if (result.IsSuccessStatusCode)
{
    var resp = await result.Content.ReadAsStringAsync();
    var html = new HtmlWebViewSource
    {
        Html = resp,
        BaseUrl = paymentGatewayUrl
    };

    //Adding Cookie
    CookieContainer cookies = new CookieContainer();
    var domain = new Uri(html.BaseUrl).Host;
    var cookie = new Cookie
    {
        Secure = true,
        Expired = false,
        HttpOnly = false,
        Name = "cookie",
        Expires = DateTime.Now.AddDays(10),
        Domain = domain,
        Path = "/"
    };
    cookies.Add(new Uri(html.BaseUrl), cookie);
    webView.Source = html;
}

Solution

  • You could modify the code of Custom Renderer like following .

    [assembly: ExportRenderer(typeof(WebView), typeof(MyWebViewRenderer))]
    namespace xxx.iOS
    {
    
        public class MyWebViewRenderer : ViewRenderer<WebView, WKWebView>
        {
            WKWebView wkWebView;
    
            protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
            {
                base.OnElementChanged(e);
    
                if (Control != null) return;
                var config = new WKWebViewConfiguration();
                wkWebView = new WKWebView(Frame, config) { NavigationDelegate = new MyNavigationDelegate() };
                SetNativeControl(wkWebView);
            }
        }
    
        public class MyNavigationDelegate : WKNavigationDelegate
        {
            public override void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
            {
    
                //get url here
                var url = webView.Url;
    
                //webView.LoadFileUrl();
    
            }
        }
    }