【问题标题】:Xamarin iOS WkWebViewRenderer displaying "X Would Like To Use Your Current Location" each timeXamarin iOS WkWebViewRenderer 每次都显示“X 想使用您当前的位置”
【发布时间】:2021-08-21 01:59:22
【问题描述】:

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

我发现以下文章可以帮助我指明正确的方向,但我对 iOS 的了解还不够,无法将其拼凑在一起。
How to prevent WKWebView to repeatedly ask for permission to access location? -- 这看起来像是我需要的,但它在 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 替换代码。

【问题讨论】:

    标签: ios swift xamarin xamarin.forms xamarin.ios


    【解决方案1】:

    具有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"
    

    渲染器 SetupScripts in OnElementChanged 覆盖:

      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
      }
    

    【讨论】:

    • 我缺少的关键部分是“locationHandler”。看起来它必须与“postMessage”之前的名称匹配才能触发 DidReceiveScriptMessage。
    【解决方案2】:

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

    如下修改你的代码

    1. 更改Handler
    userController.AddScriptMessageHandler(new myClass(), "invokeAction");
    
    1. HybridWebViewRenderer 中删除WKScriptMessageHandler 接口。
    public class HybridWebViewRenderer : WkWebViewRenderer
    
    1. 创建WKScriptMessageHandler 的子类。
        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

    【讨论】:

    • 我想我应该澄清一下 HybridWebView 的示例项目在 iOS 中确实可以工作,因为它在使用 IWKScriptMessageHandler 并调用示例 messageHandlers.invokeAction 方法时会触发 DidReceiveScriptMessage 但在调用时它不起作用messageHandlers.locationHandler。我最好的猜测是我连接错误或者我的 javascript 有问题。
    猜你喜欢
    • 1970-01-01
    • 2022-01-15
    • 1970-01-01
    • 2012-06-16
    • 1970-01-01
    • 2015-11-08
    • 1970-01-01
    • 1970-01-01
    • 2011-01-18
    相关资源
    最近更新 更多