Xamarin.forms: [Bug] When using WkWebViewRenderer, links with "target = _blank" do not respond

Created on 15 Oct 2019  路  17Comments  路  Source: xamarin/Xamarin.Forms

Description

In Xamarin.Forms it is documented that on iOS you can use WkWebViewRenderer instead of UIWebView which will be deprecated in near future.

https://docs.microsoft.com/pl-pl/xamarin/xamarin-forms/user-interface/webview?tabs=windows#performance

However when using WkWebViewRenderer there is a serious problem that when tapping on links with target = _blank nothing happens. And there is no possibility to handle that in Xamarin.Forms.

Steps to Reproduce

  1. Create a simple project with one WebView. Set the source of webview to https://sollers.eu
  2. Paste
[assembly: ExportRenderer(typeof(WebView), typeof(WkWebViewRenderer))]

in AssemblyInfo.cs in iOS project

  1. Click "Facebook" logo at the bottom of the page.
  2. Nothing happens.

Expected Behavior

Webview should redirect to Facebook page.

Actual Behavior

Nothing happens.

Basic Information

  • Version with issue: 4.2.0.848062
  • Last known good version: no
  • IDE: Visual Studio 2019 16.3.4
webview 6 iOS 馃崕 bug

Most helpful comment

@Mikilll94 First, create your own WebView inheritance in your shared project like this

using System;
using Xamarin.Forms;

namespace App1
{
    public class MyWebView : WebView
    {
    }
}

Consume it, again in your shared project, like this:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:app1="clr-namespace:App1"
             mc:Ignorable="d"
             x:Class="App1.MainPage">

    <app1:MyWebView Source="https://sollers.eu"/>

</ContentPage>

Then, in your iOS project, create a custom renderer like this

using System;
using App1;
using App1.iOS;
using Foundation;
using WebKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(MyWebView), typeof(MyWebViewRenderer))]
namespace App1.iOS
{
    public class MyWebViewRenderer : WkWebViewRenderer, IWKNavigationDelegate
    {
        protected override void OnElementChanged(VisualElementChangedEventArgs e)
        {
            base.OnElementChanged(e);

            NavigationDelegate = this;
        }

        [Export("webView:decidePolicyForNavigationAction:decisionHandler:")]
        public void DecidePolicy(WKWebView webView, WKNavigationAction navigationAction, Action<WKNavigationActionPolicy> decisionHandler)
        {
            if (navigationAction.TargetFrame == null)
            {
                webView.LoadRequest(navigationAction.Request);
            }

            decisionHandler.Invoke(WKNavigationActionPolicy.Allow);
        }
    }
}

Effectively what this does is check if the link that is clicked has target = "_blank" and if so, it resends the requests but now in the same screen.

Your adapted reproduction is here:
App1_withRenderer.zip

All 17 comments

Any chance you should attach that simple reproduction? I'll re-create it if not, but when we get a bunch of bugs to go through not having to create reproductions really saves us a bunch of time! Thanks!

Here is the reproduction project:
App1.zip

Run it and click on Facebook logo:
image

As you will notice nothing happens

Hey @kingces95! Why have you labeled this "needs-repro"? It is fairly easy to reproduce that. I have even provided a project with reproduction.

Please remove also "unverified"

Pretty sure this can be worked around using a custom renderer, subclassing WkWebViewRenderer, and overriding the DecidePolicy method to check the navigationAction parameter for the target and either opening the URL in the same window or an external one. (Some references here and here).

But that's kind of a pain for users. We should see if we have a similar issue on the other platforms (how they handle target=_blank) - it may make sense to add a property on WebView for blank target handling policy. Or, if iOS is the only place we have this issue, we should make it behave consistently with the other platforms, and provide some guidance on overriding that behavior.

Another option (if iOS is the only platform with this issue) would be to add something like BlankTargetHandlingPolicy as a Platform Specific for iOS.

Pretty sure this can be worked around using a custom renderer, subclassing WkWebViewRenderer, and overriding the DecidePolicy method to check the navigationAction parameter for the target and either opening the URL in the same window or an external one. (Some references here and here).

But that's kind of a pain for users. We should see if we have a similar issue on the other platforms (how they handle target=_blank) - it may make sense to add a property on WebView for blank target handling policy. Or, if iOS is the only place we have this issue, we should make it behave consistently with the other platforms, and provide some guidance on overriding that behavior.

Another option (if iOS is the only platform with this issue) would be to add something like BlankTargetHandlingPolicy as a Platform Specific for iOS.

Could you provide an example code of this custom renderer?

@Mikilll94 First, create your own WebView inheritance in your shared project like this

using System;
using Xamarin.Forms;

namespace App1
{
    public class MyWebView : WebView
    {
    }
}

Consume it, again in your shared project, like this:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:app1="clr-namespace:App1"
             mc:Ignorable="d"
             x:Class="App1.MainPage">

    <app1:MyWebView Source="https://sollers.eu"/>

</ContentPage>

Then, in your iOS project, create a custom renderer like this

using System;
using App1;
using App1.iOS;
using Foundation;
using WebKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(MyWebView), typeof(MyWebViewRenderer))]
namespace App1.iOS
{
    public class MyWebViewRenderer : WkWebViewRenderer, IWKNavigationDelegate
    {
        protected override void OnElementChanged(VisualElementChangedEventArgs e)
        {
            base.OnElementChanged(e);

            NavigationDelegate = this;
        }

        [Export("webView:decidePolicyForNavigationAction:decisionHandler:")]
        public void DecidePolicy(WKWebView webView, WKNavigationAction navigationAction, Action<WKNavigationActionPolicy> decisionHandler)
        {
            if (navigationAction.TargetFrame == null)
            {
                webView.LoadRequest(navigationAction.Request);
            }

            decisionHandler.Invoke(WKNavigationActionPolicy.Allow);
        }
    }
}

Effectively what this does is check if the link that is clicked has target = "_blank" and if so, it resends the requests but now in the same screen.

Your adapted reproduction is here:
App1_withRenderer.zip

@jfversluis Thanks, a lot :)

@jfversluis
Your solution works, but Navigating and Navigated events are not called. Maybe do you have a solution for that?

Ugh, yeah, OK that sucks. The only quick way I see is to basically take this whole delegate from the actual renderer and add the piece from my renderer in the project I added.

Since our delegate is private you can't create an inheritance from that

@jfversluis
Could you have some example implementation? Because I was trying to copy this navigation delegate and unfortunately this is not so easy to implement.

@Mikilll94 Again, this sucks. I've been trying to look into it to make a workaround for now, but you need to pull in a lot of code from the repo to make this work because a lot of the methods and fields you need are private or internal.

Therefore I just decided to implement the fix and make a PR. That will mean that hopefully it is added soon, but I don't have a decent workaround for you right now. If this is something that is important to you, you might want to stick to the UIWebView for now until this fix is incorporated.

@jfversluis
Thanks for your response :)

closed by #8281

@jfversluis
Your solution works, but Navigating and Navigated events are not called. Maybe do you have a solution for that?

This worked for me:

```
[assembly:ExportRenderer (typeof(MyCustomWebview), typeof(MyCustomWebviewRendererIOS))]
namespace XYZ.iOS
{
public class MyCustomWebviewRendererIOS: WkWebViewRenderer, IWKNavigationDelegate
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (NativeView != null)
{
WeakUIDelegate = this;
}
}
[Export("webView:createWebViewWithConfiguration:forNavigationAction:windowFeatures:")]
public WKWebView CreateWebView(WKWebView webView, WKWebViewConfiguration configuration, WKNavigationAction action, WKWindowFeatures features)
{
if (action.Request.Url != null)
{
webView.LoadRequest(new NSUrlRequest(action.Request.Url));
}
return null;
}
}
}

Has this bug fix been released yet? I'm still seeing the issue where links with Target="_blank" are being ignored.

@dpuckett Yes, this fix has been released. If you're still seeing this issue on the latest version of Forms, please open a new issue and we'll take a look.

Was this page helpful?
0 / 5 - 0 ratings