【发布时间】:2020-06-19 08:00:09
【问题描述】:
昨天我花了几个小时找出为什么我无法访问我的 Hybrid wkWebView 的(显然工作的)参数。 在正确触发的 DidFinishNavigation 覆盖下的 iOS 部分中,我在尝试 EvaluateJavaScriptAsync 甚至访问 Source 参数时总是遇到 NullObjectReference 异常。
然后我终于发现我看错了对象:我访问的是 Forms HybridWebView 对象(在“链”中向上)而不是 iOS webview 对象(从渲染器向下)。在下面的代码中,来自 DidFinishNavingation 的“webView”参数而不是来自 webViewRenderer.Element 的 _wkWebView(参见最后一个代码部分)
现在我的代码正在运行,但我仍然想知道我对平台特定集成的总体概念是否正确,或者表单 hybridwebview 中可用的表单函数(例如 EvaluateJavaScript)是否应该直接运行?我是否缺少一些链接来将这些链接与底层 iOS webview 连接起来?
我的 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);
}
}
最后是 iOS 类中的 DidFinishLoading 事件处理程序:
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);
}
}
}
【问题讨论】:
-
既然您的代码运行良好,您现在想做什么?实现一些功能或其他什么?
-
我的问题是,像 EvaluateJavaScript 这样的 Forms webview 函数是否仍应与自定义 webview 渲染器一起使用,或者,一旦您拥有自定义渲染器,您是否需要自己实现所有内容?通常你会调用“base.xx”来传递重载类的默认实现,但是对于 didFinishNavigation 函数是不允许的。
标签: xamarin wkwebview custom-renderer