在iOS上为内部导航引发的Xamarin.Forms.WebView.Navigating事件

假设您要阻止用户从Xamarin.Forms.WebView导航到外部页面。

public App () { var webView = new WebView { Source = new HtmlWebViewSource { Html = "

Hello world

Can't escape!" } }; webView.Navigating += WebView_Navigating; MainPage = new ContentPage { Content = webView }; } private void WebView_Navigating(object sender, WebNavigatingEventArgs e) { // we don't want to navigate away from our page // open it in a new page instead etc. e.Cancel = true; }

这适用于Windows和Android。 但是在iOS上,它根本不加载!

在iOS上,即使从HtmlWebViewSource加载源代码时,也会引发Navigating事件,其URL类似于file:///Users/[user]/Library/Developer/CoreSimulator/Devices/[deviceID]/data/Containers/Bundle/Application/[appID]/[appName].app/

好吧,所以你可以用这样的东西解决这个问题:

 private void WebView_Navigating(object sender, WebNavigatingEventArgs e) { if (e.Url.StartsWith("file:") == false) e.Cancel = true; } 

该页面最终在iOS上加载。 好极了。 可是等等! 嵌入的YouTubevideo无法加载! 那是因为Navigating事件被引发用于嵌入式资源的内部导航 ,例如iframe甚至外部脚本(如Twitter的 ),但仅限iOS!

我找不到确定Navigating事件是从内部导航引发还是因为用户单击链接的方法。

怎么解决这个问题?

我不确定是否可以开箱即用Xamarin Forms进行检测,但使用自定义渲染器可以轻松确定导航类型。 在您的自定义iOS渲染器中,分配WebViewDelegate并在该Delegate类中,覆盖ShouldStartLoad()如下所示:

 public class CustomWebViewRenderer : WebViewRenderer { #region Properties public CustomWebView CustomWebViewItem { get { return Element as CustomWebView; } } #endregion protected override void OnElementChanged(VisualElementChangedEventArgs e) { base.OnElementChanged(e); if(e.OldElement == null) { Delegate = new CustomWebViewDelegate(); //Assigning the delegate } } } internal class CustomWebViewDelegate : UIWebViewDelegate { public override bool ShouldStartLoad(UIWebView webView, NSUrlRequest request, UIWebViewNavigationType navigationType) { if(navigationType == UIWebViewNavigationType.LinkClicked) { //To prevent navigation when a link is click, return false return false; } return true; } } 

你也可以将一个bool属性甚至一个枚举表示回你的Xamarin Forms WebView ,它会说明Navigating事件是来自被点击的链接还是来自其他东西,尽管也需要自定义渲染器。

 private bool isNavigated = false; public CustomWebView() { if (Device.OS == TargetPlatform.Android) { // always true for android isNavigated = true; } Navigated += (sender, e) => { isNavigated = true; }; Navigating += (sender, e) => { if (isNavigated) { try { var uri = new Uri(e.Url); Device.OpenUri(uri); } catch (Exception) { } e.Cancel = true; } }; }