Исключение выброса Xamarin HybridWebView в EvaluateJavaScript - PullRequest
0 голосов
/ 19 июня 2020

Вчера я потратил несколько часов, выясняя, почему я не могу получить доступ к (явно работающим) параметрам моего Hybrid wkWebView. В части iOS под правильно запущенным переопределением DidFinishNavigation я всегда получал исключение NullObjectReference при попытке EvaluateJavaScriptAsyn c или даже доступа к параметру Source.

Затем я наконец обнаружил, что смотрю на неправильный объект: я обращался к объекту Forms HybridWebView (вверху в «цепочке») вместо объекта iOS webview (вниз от средства визуализации). В приведенном ниже коде параметр «webView» из DidFinishNavingation заменяет _wkWebView из webViewRenderer.Element (см. Последнюю часть кода)

Теперь мой код работает, но мне все еще интересно, есть ли моя общая концепция специфика платформы c интеграция правильная или функции форм, доступные в формах hybridwebview (например, Evaluate JavaScript), должны работать напрямую? Мне не хватает какой-то ссылки для подключения их к базовому веб-просмотру iOS?

Код моих общих форм для HybidWebView:

public class MyWebView : WebView
{
    public event EventHandler SwipeLeft;
    public event EventHandler SwipeRight;

    public void OnSwipeLeft() =>
        SwipeLeft?.Invoke(this, null);

    public void OnSwipeRight() =>
        SwipeRight?.Invoke(this, null);

    public static readonly BindableProperty UrlProperty = BindableProperty.Create(
        propertyName: "Url",
        returnType: typeof(string),
        declaringType: typeof(MyWebView),
        defaultValue: default(string));

    public string Url
    {
        get { return (string)GetValue(UrlProperty); }
        set
        {
            SetValue(UrlProperty, value);
            NotifyPropertyChanged(nameof(HTML));
        }
    }

    public static readonly BindableProperty HTMLProperty = BindableProperty.Create(
        propertyName: "HTML",
        returnType: typeof(string),
        declaringType: typeof(MyWebView),
        defaultValue: default(string));

    public string HTML
    {
        get { return (string)GetValue(HTMLProperty); }
        set
        {
            SetValue(HTMLProperty, value);
            NotifyPropertyChanged(nameof(HTML));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

и подключенная часть iOS для Рендерер:

    public class MyWebViewRenderer : ViewRenderer<MyWebView, WKWebView>
{
    WKWebView _wkWebView;

    protected override void OnElementChanged(ElementChangedEventArgs<MyWebView> e)
    {
        System.Console.WriteLine("--- Loading wkWebView ---");
        base.OnElementChanged(e);

        if (Control == null)
        {
            Console.WriteLine("+++ WKW: Control null -> init");

            var config = new WKWebViewConfiguration();
            _wkWebView = new WKWebView(Frame, config);

            SetNativeControl(_wkWebView);
            _wkWebView.NavigationDelegate = new ExtendedWKWebViewDelegate(this);

        }

        if (e.NewElement != null)
        {
            Console.WriteLine("+++ WKW: Control exists -> init GestureRecognizers");

            UISwipeGestureRecognizer leftgestureRecognizer = new UISwipeGestureRecognizer(this, new Selector("SwipeEvent:"));

            leftgestureRecognizer.Direction = UISwipeGestureRecognizerDirection.Left;

            UISwipeGestureRecognizer rightgestureRecognizer = new UISwipeGestureRecognizer(this, new Selector("SwipeEvent:"));
            rightgestureRecognizer.Direction = UISwipeGestureRecognizerDirection.Right;

            leftgestureRecognizer.Delegate = new MyWebViewDelegate();
            rightgestureRecognizer.Delegate = new MyWebViewDelegate();

            this.AddGestureRecognizer(leftgestureRecognizer);
            this.AddGestureRecognizer(rightgestureRecognizer);
        }

        NSUrl baseURL = new NSUrl(App.dirNews, true);
        string viewFile = Path.Combine(App.dirNews, "view.html");
        NSUrl fileURL = new NSUrl(viewFile, false);

        if (Element?.HTML != null && Element?.HTML != "")
        {
            System.Console.WriteLine("--- Showing HTTP content ---");
            File.WriteAllText(viewFile, Element.HTML, System.Text.Encoding.UTF8);
            Control.LoadFileUrl(fileURL, baseURL);
        }
        else if (Element?.Url != null && Element?.Url != "")
        {
            System.Console.WriteLine("--- Loading Web page ---");
            System.Console.WriteLine("--- " + Element.Url + " ---");
            NSUrlRequest myRequest = new NSUrlRequest(new NSUrl(Element.Url), NSUrlRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData, 120);
            Control.LoadRequest(myRequest);
        }

    }

и, наконец, обработчик события DidFinishLoading в классе iOS:

    class ExtendedWKWebViewDelegate : WKNavigationDelegate
{

    MyWebViewRenderer webViewRenderer;

    public ExtendedWKWebViewDelegate(MyWebViewRenderer _webViewRenderer = null)
    {
        webViewRenderer = _webViewRenderer ?? new MyWebViewRenderer();
    }

    [Export("webView:didFinishNavigation:")]
    public override async void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
    {
        try
        {
            var _webView = webViewRenderer.Element as WebView;
            if (_webView != null)
            {
                await System.Threading.Tasks.Task.Delay(100); // wait here till content is rendered
                _webView.HeightRequest = (double)webView.ScrollView.ContentSize.Height;

                //### get html from site
                var myRes = await webView.EvaluateJavaScriptAsync("document.body.innerHTML");

                //### Flag complete status
                MessagingCenter.Send<object, string>(this, "didFinishNavigation", myRes.ToString());

            }
        }

        catch (Exception ex)
        {
            //Log the Exception
            Console.WriteLine("*** CustomWebView Exception: " + ex.Message + "/" + ex.InnerException);
        }


    }

}

1 Ответ

1 голос
/ 19 июня 2020

Когда мы вызывали строку,

SetNativeControl(_wkWebView);

WebView, который вы определяете в Forms, был снова инициализирован. Поэтому вам нужно реализовать большую часть функций в Custom Renderer . Особенно в WebView из iOS, если вы хотите добавить слушателя в его жизненный цикл или вызвать какой-нибудь метод JS, лучше всего (или единственный) реализовать его в Custom Renderer .

...