Xamarin iOS WkWebViewRenderer 每次显示 "X Would Like To Use Your Current Location"

Xamarin iOS WkWebViewRenderer displaying "X Would Like To Use Your Current Location" each time

我有一个 Xamarin Forms 应用程序需要调用包含自定义 Bing 地图的网页,并且在该网页上它需要能够获取用户的当前位置。
该应用程序已经拥有 Android 和 iOS 所需的“应用程序”权限,我可以毫无问题地在应用程序内部获取用户位置。
当我调用具有自定义 Bing 地图的外部网页时,它确实允许我在 Android 和 iOS 中获取用户位置,但在 iOS 上它会询问用户“网站...想使用您的当前位置。不允许/确定”每次他们从我的 Xamarin 应用程序内部访问网页时。

我发现以下文章可以帮助我找到正确的方向,但我对 iOS 的理解还不够,无法将它们拼凑起来。
-- 这看起来像我需要的,但它在 iOS 中并且不使用较新的 iOS 应用程序所需的 WkWebViewRender。
https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/custom-renderer/hybridwebview -- 这向我展示了如何将“脚本”添加到 iOS 端,但我无法触发 DidReceiveScriptMessage 方法。

这是我当前的 WkWebViewRender 实现。

public class HybridWebViewRenderer : WkWebViewRenderer, IWKScriptMessageHandler
{
    public HybridWebViewRenderer() : this(new WKWebViewConfiguration())
    {
    }        

    const string JavaScriptFunctionTest = 
        "navigator.geolocation.getCurrentPosition = function(success, error, options) {window.webkit.messageHandlers.locationHandler.postMessage('getCurrentPosition');};";

    WKUserContentController userController;

    public HybridWebViewRenderer(WKWebViewConfiguration config) : base(config)
    {
        try
        {
            userController = config.UserContentController;
            var script = new WKUserScript(new NSString(JavaScriptFunctionTest), injectionTime: WKUserScriptInjectionTime.AtDocumentEnd, isForMainFrameOnly: true);
            userController.AddUserScript(script);
            userController.AddScriptMessageHandler(this, "locationHandler");
        }
        catch (System.Exception ex)
        {
                
        }
    }

    public void DidReceiveScriptMessage(WKUserContentController userContentController, WKScriptMessage message)
    {
        var msg = message.Body.ToString();
        System.Diagnostics.Debug.WriteLine(msg);
    }
}

这是我的 html 页面中唯一的 javascript 功能。

function StartTracking() {
    //Add a pushpin to show the user's location.
    userPin = new Microsoft.Maps.Pushpin(map.getCenter(), { visible: true });
    map.entities.push(userPin);

    //Watch the users location.
    watchId = navigator.geolocation.watchPosition(UsersLocationUpdated);
}

function UsersLocationUpdated(position) {
    var loc = new Microsoft.Maps.Location(
        position.coords.latitude,
        position.coords.longitude);

    //Update the user pushpin.
    userPin.setLocation(loc);
    userPin.setOptions({ visible: true });

    //Center the map on the user's location.
    map.setView({ center: loc });
}

function StopTracking() {
    // Cancel the geolocation updates.
    navigator.geolocation.clearWatch(watchId);

    //Remove the user pushpin.
    map.entities.clear();
}

https://gist.github.com/hayahyts/2c369563b2e9f244356eb6228ffba261 非常接近我的需要,但我一定是做错了什么。
如果我使用

,则不会调用 DidReceiveScriptMessage
const string JavaScriptFunction = 
    "navigator.geolocation.getCurrentPosition " + 
    " = function(success, error, options) " + 
    "{window.webkit.messageHandlers.locationHandler.postMessage('getCurrentPosition');};";

但是如果我使用

,DidReceiveScriptMessage 确实会被调用
const string JavaScriptFunction = 
    "function invokeCSharpAction(data){window.webkit.messageHandlers.invokeAction.postMessage(data);}";

所以我还不确定哪里出了问题,但一定是 locationHandler 替换代码。

尝试创建一个继承自 WKScriptMessageHandler 的新 class 来处理 DidReceiveScriptMessage 方法。

修改你的代码如下

  1. 更改Handler
userController.AddScriptMessageHandler(new myClass(), "invokeAction");
  1. HybridWebViewRenderer 中删除 WKScriptMessageHandler 界面。
public class HybridWebViewRenderer : WkWebViewRenderer
  1. 创建 WKScriptMessageHandler 的子class .
    public class myClass : WKScriptMessageHandler
    {

        public override void DidReceiveScriptMessage(WKUserContentController userContentController, WKScriptMessage message)
        {
            //_webview.InvokeAction(message.Body.ToString());
        }
    }

检查类似问题:https://forums.xamarin.com/discussion/169893/wkscriptmessagehandler-is-not-firing .

具有 ExportRenderer 属性的渲染器命名空间:

[assembly: ExportRenderer(typeof(WebView), typeof(MyNamespace.MyWebViewRenderer))]
namespace MyNamespace
{

渲染器声明参考 IWKScriptMessageHandler:

 public class MyWebViewRenderer : WkWebViewRenderer, IWKScriptMessageHandler
 {

渲染器字段:

  const string MessageHandlerName = "locationHandler";
  const string JavaScriptFunction =
        "navigator.geolocation.getCurrentPosition = function(success, error, options) {window.webkit.messageHandlers.locationHandler.postMessage('getCurrentPosition');};";
  //const string HtmlSource = "html containing navigator.geolocation.getCurrentPosition"

OnElementChanged 中的渲染器 SetupScripts 覆盖:

  protected override void OnElementChanged(VisualElementChangedEventArgs e)
  {
    base.OnElementChanged(e);
    if (e.NewElement != null)
    {
      SetupScripts(Configuration);
      //LoadHtmlString(HtmlSource, null); //example loading page
    }
  }

  void SetupScripts(WKWebViewConfiguration wkWebViewConfig)
  {
    wkWebViewConfig.UserContentController.AddScriptMessageHandler(this, MessageHandlerName);
    var jsFunction = new WKUserScript(new NSString(JavaScriptFunction), WKUserScriptInjectionTime.AtDocumentEnd, false);            
    wkWebViewConfig.UserContentController.AddUserScript(jsFunction);
  }

IWKScriptMessageHandler 的渲染器实现:

  //IWKScriptMessageHandler
  public void DidReceiveScriptMessage(WKUserContentController userContentController, WKScriptMessage message)
  {
     //should be invoked when getCurrentPosition in HtmlSource is invoked
  }