Xamarin.iOS可以’;t从WebView导航和导航事件控制iOS活动指示器

Xamarin.iOS可以’;t从WebView导航和导航事件控制iOS活动指示器,webview,xamarin.ios,navigation,Webview,Xamarin.ios,Navigation,我有一个ContentPage,它有一个WebView和一个ActivityIndicator。有用于导航和导航事件的事件处理程序来激活和取消激活ActivityIndicator。在iOS上,导航事件会激发,但导航事件不会持续激发。这通常会使ActivityIndicator处于永久激活状态。通过导航到,可以复制此内容 有人建议我使用自定义web渲染器响应DidFinishNavigation和DidFailNavigation,并发送MessagingCenter消息以关闭活动指示器。我添加

我有一个ContentPage,它有一个WebView和一个ActivityIndicator。有用于导航和导航事件的事件处理程序来激活和取消激活ActivityIndicator。在iOS上,导航事件会激发,但导航事件不会持续激发。这通常会使ActivityIndicator处于永久激活状态。通过导航到,可以复制此内容

有人建议我使用自定义web渲染器响应DidFinishNavigation和DidFailNavigation,并发送MessagingCenter消息以关闭活动指示器。我添加了这个,它(大部分)起了作用,但无论出于什么原因(可能是XF 5的更新或WKWebView的更改),它都停止了工作。DidFinishNavigation仍在自定义渲染器中启动,但现在Navigation和Navigated从未在WebView中启动。DidFailNavigation从不触发,页面呈现完全正确,因此我不认为导航失败是问题所在。如果删除自定义渲染器,它将返回到导航事件触发,并且导航仅在某些时间触发

我将非常感谢任何关于这方面的见解

<ContentPage xmlns = "http://xamarin.com/schemas/2014/forms"
                xmlns: x = "http://schemas.microsoft.com/winfx/2009/xaml"
                xmlns: vm = "clr-namespace:FFXPubXam.ViewModels"
                x: Class = "FFXPubXam.Views.WebPage"
                x: Name = "root"
                Shell.NavBarIsVisible = "false" >
    < ContentPage.Content >
        < Grid >
            < WebView x: Name = "WebViewControl"
                        Source = "{Binding Url}"
                        Navigating = "WebViewControl_Navigating"
                        Navigated = "WebViewControl_Navigated" />
            < ActivityIndicator x: Name = "activity"
                                IsRunning = "False"
                                IsEnabled = "False"
                                IsVisible = "False"
                                HeightRequest = "40"
                                WidthRequest = "100"
                                VerticalOptions = "CenterAndExpand"
                                HorizontalOptions = "CenterAndExpand"
                                Color = "{DynamicResource FFX_Blue4}"
                                BackgroundColor = "Transparent”/>
            </ Grid >
    </ ContentPage.Content >
</ ContentPage >

[QueryProperty("Url", "url")]
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class WebPage : ContentPage
{
    private string baseUrl = string.Empty;
    private bool subscribed = false;

    private string url;

    public string Url
    {
        set
        {
            if (value != null)
            {
                url = Uri.UnescapeDataString(value);
            }
        }
        get
        {
            return url;
        }
    }

    public WebPage()
    {
        InitializeComponent();
        BindingContext = new WebPageViewModel();
    }

    public WebPage(string arg)
    {
        Url = arg;

        InitializeComponent();
        BindingContext = new WebPageViewModel();
    }

    protected override void OnAppearing()
    {
        if (baseUrl == string.Empty)
        {
            if (Url != null)
            {
                baseUrl = Url;
            }
        }

        if (WebViewControl.Source?.ToString() != baseUrl)
        {
            WebViewControl.Source = baseUrl;
        }

        base.OnAppearing();

        Logging.Write(LogType.Info, $"OnAppearing: Url={url}");

        if (!subscribed) SubScribe();

        ToggleActivityIndicator(true);
    }

    private void WebViewControl_Navigating(object sender, WebNavigatingEventArgs e)
    {
        Logging.Write(LogType.Info, $"Navigating: Url={url}");
        ToggleActivityIndicator(true);
    }

    private void WebViewControl_Navigated(object sender, WebNavigatedEventArgs e)
    {
        Logging.Write(LogType.Info, $"Navigated: Url={url}");
        ToggleActivityIndicator(false);
    }

    private void ToggleActivityIndicator(bool state)
    {
        Logging.Write(LogType.Info, $"ToggleActivityIndicator: state={state} stack={new StackTrace()}");
        activity.IsRunning = state;
        activity.IsEnabled = state;
        activity.IsVisible = state;
    }

    private void SubScribe()
    {
        Logging.Write(LogType.Info, $"Subscribe: call stack={new StackTrace()}");

        MessagingCenter.Subscribe<object>(this, "End", (sender) =>
        {
            ToggleActivityIndicator(false);
        });
    }
}
    
[assembly: ExportRenderer(typeof(WebView), typeof(CustomWebViewRenderer))]
namespace FFXPubXam.iOS.Renderers
{
    class MyDelegate : WKNavigationDelegate
    {
        public override void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
        {
            MessagingCenter.Send<object>(this, "End");
            Logging.Write(LogType.Info, $"DidFinishNavigation: after Send");
        }
        public override void DidFailNavigation(WKWebView webView, WKNavigation navigation, NSError error)
        {
            MessagingCenter.Send<object, string>(this, "End", error.ToString());
        }
    }

    class CustomWebViewRenderer : WkWebViewRenderer
    {
        protected override void OnElementChanged(VisualElementChangedEventArgs e)
        {
            base.OnElementChanged(e);
            if (e.NewElement != null)
            {
                this.NavigationDelegate = new MyDelegate();
            }
        }
    }
}





[查询属性(“Url”、“Url”)]
[XamlCompilation(XamlCompilationOptions.Compile)]
公共部分类网页:ContentPage
{
私有字符串baseUrl=string.Empty;
private bool subscribed=false;
私有字符串url;
公共字符串Url
{
设置
{
if(值!=null)
{
url=Uri.UnescapeDataString(值);
}
}
得到
{
返回url;
}
}
公共网页()
{
初始化组件();
BindingContext=新的WebPageViewModel();
}
公共网页(字符串arg)
{
Url=arg;
初始化组件();
BindingContext=新的WebPageViewModel();
}
出现时受保护的覆盖无效()
{
if(baseUrl==string.Empty)
{
如果(Url!=null)
{
baseUrl=Url;
}
}
if(WebViewControl.Source?.ToString()!=baseUrl)
{
WebViewControl.Source=baseUrl;
}
base.OnAppearing();
Write(LogType.Info,$”OnAppearing:Url={Url});
如果(!subscribed)SubScribe();
ToggleActivityIndicator(真);
}
私有void WebViewControl_导航(对象发送方,WebNavigatingEventArgs e)
{
Write(LogType.Info,$“导航:Url={Url}”);
ToggleActivityIndicator(真);
}
私有void WebViewControl_已导航(对象发送方,WebNavigatedEventArgs e)
{
Write(LogType.Info,$“Navigated:Url={Url}”);
ToggleActivityIndicator(假);
}
私有无效TogleActivityIndicator(布尔状态)
{
Write(LogType.Info,$“ToggleActivityIndicator:state={state}stack={new StackTrace()}”);
activity.IsRunning=状态;
activity.IsEnabled=状态;
activity.IsVisible=状态;
}
私有无效订阅()
{
Write(LogType.Info,$“Subscribe:callstack={newstacktrace()}”);
MessagingCenter.Subscribe(此“结束”,(发件人)=>
{
ToggleActivityIndicator(假);
});
}
}
[程序集:ExportRenderer(typeof(WebView)、typeof(CustomWebViewRenderer))]
命名空间FFXPubXam.iOS.Renderers
{
类MyDelegate:WKNavigationDelegate
{
公共覆盖无效DidFinishNavigation(WKWebView、WKNavigation导航)
{
发送(此“结束”);
Logging.Write(LogType.Info,$“didfishnavigation:after Send”);
}
公共覆盖无效导航(WKWebView webView、WKNavigation导航、N错误)
{
Send(这个“End”,error.ToString());
}
}
类CustomWebViewRenderer:WkWebViewRenderer
{
受保护的覆盖无效OnElementChanged(VisualElementChangedEventArgs e)
{
基础。一个要素发生变化(e);
if(例如NewElement!=null)
{
this.NavigationDelegate=新的MyDelegate();
}
}
}
}

我测试了您在XF5下提供的URL。尽管花费了很长时间,但还是触发了WebViewControl\u导航。我还测试了URL“https://stackoverflow.com/“,WebViewControl_导航迅速响应

恐怕这个问题的根本原因是“费尔法克斯县”网站

此外,如果要在自定义渲染器中调用“ToggleActivityIndicator”,MessagingCenter不是一个好选择。您可以通过事件更有效地实现它

在CustomWebView类中定义事件:

public class CustomWebView : WebView
{
    public delegate void NavigateDel(bool flag);

    public event NavigateDel NavigateEvent;

    public void CallNavigate(bool flag)
    {
        NavigateEvent(flag);
    }
}
然后在“网页”中订阅活动:

如果已创建自定义呈现程序,则无需订阅导航和导航。要在加载页面时显示指示器,还需要覆盖“DidStartProvisi”
public WebPage()
{
    InitializeComponent();
    BindingContext = new WebPageViewModel();
    WebViewControl.NavigateEvent += ToggleActivityIndicator;
}
class MyDelegate : WKNavigationDelegate
{
    CustomWebView customView;

    public MyDelegate(CustomWebView view)
    {
        customView = view;
    }

    public override void DidStartProvisionalNavigation(WKWebView webView, WKNavigation navigation)
    {
        customView.CallNavigate(true);
    }

    public override void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
    {
        customView.CallNavigate(false);
    }
    public override void DidFailNavigation(WKWebView webView, WKNavigation navigation, NSError error)
    {
    }
}

class CustomWebViewRenderer : WkWebViewRenderer
{
    protected override void OnElementChanged(VisualElementChangedEventArgs e)
    {
        base.OnElementChanged(e);
        if (e.NewElement != null)
        {
            CustomWebView control = (CustomWebView)Element;
            this.NavigationDelegate = new MyDelegate(control);
        }
    }
}