【问题标题】:Reserve the loaded page content of WebView when switch away from current page in a Master / Detail layout in Xamarin Forms在 Xamarin Forms 的 Master/Detail 布局中从当前页面切换时保留 WebView 的加载页面内容
【发布时间】:2017-05-15 05:23:33
【问题描述】:

我正在使用Master / Detail 布局来构建导航菜单和几页。其中一个页面是WebView 控件,仅此而已。当我从导航菜单切换离开这个 WebView 页面然后切换回来时,WebView 的内容消失了,状态也消失了。这发生在iOSAndroid 上。

但是,如果我从 WebView 页面导航到完全不同的页面(非主/详细页面),在返回 WebView 页面后,一切都很好.保留所有内容和状态。

我必须重新加载页面,但用户的操作会丢失。有没有办法保留页面的内容和状态并在不重新加载页面的情况下恢复它们?

【问题讨论】:

  • 您需要在 OnAppearing() 方法中设置 webview 的内容或来源
  • 不,这正是我不想做的,因为我只想加载页面一次,即使在离开该页面然后导航回来之后也将内容保留在那里。
  • 为此,您可以在页面的覆盖方法 OnDisappearing() 上将 webview 的源设置为“about:blank”
  • 你确定?由于将其设置为 about:blank 显然会破坏前一页。我不想破坏页面,我想保存页面状态
  • 看我的帖子,你得修改MasterDetail的源码才能持久化原生的webview

标签: c# xamarin webview xamarin.ios xamarin.forms


【解决方案1】:

不幸的是,您需要修改 source code of Xamarin.Forms.MasterDetailPage 以提供所需的行为。问题是当从MasterDetail中移除Detail页面时(从Webview切换到另一个页面),所有的原生控件都被释放了;即使您保留对 Xamarin.Forms.WebviewXamarin.Forms.Page 对象的硬引用。

在源码中可以看到,如果_detail属性不为null,则从MasterDetailPage.PageController.InternalChildren中移除。您可能需要检查包含Xamarin.Forms.WebviewXamarin.Forms.Page,并确保它没有从页面控制器中删除。这将保留本机 webview 和 webview 处理程序,因此不会重新加载任何内容。需要注意的几点:

  1. 确保您也没有将您的 web 视图页面重新添加到 MasterDetail PageController.InternalChildren
  2. webview 页面将保持加载到内存中。这很重要,因为如果您的网站执行大量 javascript、存储或更多内容,您应该注意,即使未显示它也会保留在内存中

您可以通过创建一个新的 Forms 项目并将App 类代码替换为以下代码来验证所有这些:

public class App : Application
{
    public App()
    {
        MainPage = new MasterDetail();
    }
}

public class MasterDetail : MasterDetailPage
{
    static WebViewPage persistentWebPage = new WebViewPage();

    public MasterDetail()
    {
        var masterPage = new MasterPage();
        persistentWebPage = new WebViewPage();

        Master = masterPage;
        Detail = persistentWebPage;

        masterPage.listview.ItemSelected += (sender, e) =>
        {
            var item = e.SelectedItem as string;

            if (string.IsNullOrEmpty(item))
                return;

            switch (item)
            {
                case "1":
                    Detail = persistentWebPage;
                    break;
                default:
                    Detail = new ContentPage { BackgroundColor = Color.Red };
                    break;
            }

            masterPage.listview.SelectedItem = null;
            IsPresented = false;
        };
    }
}

public class MasterPage : ContentPage
{
    public ListView listview = new ListView { ItemsSource = new string[] { "1", "2", "3" } };

    public MasterPage()
    {
        Title = "Master";
        Content = listview;
    }
}

public class WebViewPage : ContentPage
{
    public static WebView webView = new WebView { Source = "https://www.google.com/" };

    public WebViewPage()
    {
        Content = webView;
    }
}

接下来在 Android 项目中创建一个渲染器(iOS 也可以,我刚做了 Android)并复制以下代码:

public class WebViewRenderer_Droid : WebViewRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
    {
        if (e.NewElement == null)
            Console.WriteLine("*********e.NewElement is null");
        else
            Console.WriteLine("*********e.NewElement is not null");

        if (e.OldElement == null)
            Console.WriteLine("*********e.OldElement is null");
        else
            Console.WriteLine("*********e.OldElement is not null");

        if (Control == null)
            Console.WriteLine("*********Control is null");
        else
            Console.WriteLine("*********Control is not null");

        base.OnElementChanged(e);
    }
}

运行项目并从 web 视图页面切换回来。您会看到Controle.OldElement 在某一时刻都为空。这证明即使有硬引用,您的本地处理程序也会被处理。这是因为您无法控制 Xamarin.Forms 如何处理本机控件引用。 From the source code of Xamarin.Forms,我们可以看到,如果 Control 为空,那么一切都会重新创建。

我希望这会有所帮助!深入挖掘来解决这个问题很有趣。

【讨论】:

  • 嗨,我想你找到了它的原因,但解决方案显然比你建议的要多。我尝试了您的建议更改,但仍然无效。但无论如何,很棒的发现
【解决方案2】:

为了解决这个问题,我创建了一个自定义 webview 视图渲染器。当我在渲染器中创建新的 webview 时(例如 new WKWebView(Frame, opts)),我将其替换为覆盖 Dispose 的 WKWebview 的子类,使其什么也不做。根据需要跟踪 OnElementChanged 中的原始 webview。

【讨论】:

  • 尝试更清楚地解释为什么这是问题的答案
【解决方案3】:

在您的App 类中,创建包含WebViewContentPagestatic 实例。在App 类中创建ContentPage 作为静态变量将保留其状态并确保它打开到用户最近查看的同一网页。

示例代码

using Xamarin.Forms;

namespace PersistWebViewSample
{
    public class StartPage : ContentPage
    {
        public StartPage()
        {
            var navigateToWebViewButton = new Button
            {
                Text = "Open Persistent Web View"
            };
            navigateToWebViewButton.Clicked += async (sender, e) => await Navigation.PushAsync(App.PersistentWebView);

            Title = "Start";

            Content = navigateToWebViewButton;
        }
    }

    public class App : Application
    {
        static readonly string xamarinUrl = "https://www.xamarin.com/";

        public App()
        {

            MainPage = new NavigationPage(new StartPage());
        }

        public static ContentPage PersistentWebView { get; } = new ContentPage
        {
            Title = "Persistent Web View",
            Content = new WebView
            {
                Source = xamarinUrl
            }
        };
    }
}

【讨论】:

  • 似乎不适用于主/详细页面导航
  • 它似乎保存了 webView 的 URL,但仍在重新加载该 URL。重新加载就像是对页面的新访问
【解决方案4】:

我找到了一个可行的解决方案,虽然更像是一个 hack。这不是在触摸菜单项时导航到主/详细信息中的子页面,而是导航到另一个页面,这意味着向下 1 级。当用户从该页面导航回来时,所有内容都将被保留。但这样一来,导航菜单将不再存在。但这没关系。

【讨论】:

    【解决方案5】:

    导航控制器将现有的 UIViewController 保留在导航堆栈上,但是当您弹出 UIViewController 时,它会从堆栈中删除视图控制器,因此其内容会被删除。

    要保留它,您需要有一个共享的 WebViewController 实例,并且始终使用相同的 WebViewController。

    在您的 WebView 控制器中编写以下代码以创建共享实例:

    private static ViewController vc;
    
    public static WebViewController SharedInstance()
    {
        if (vc == null)
        {
            var storyboard = UIStoryboard.FromName("MainStoryboard", null);
            vc = storyboard.InstantiateViewController("WevViewController") as WevViewController;
        }
    
        return vc;
    }
    

    当你想显示网页视图控制器时,使用下面

    public void DisplayWebview()
    {
        WevViewController webvw = WevViewController.SharedInstance();
        NavigationController.PushViewController(webvw,true);
    }
    

    【讨论】:

    • 感谢您的回答,但我使用的是 Xamarin Forms,它是 Android/iOS api 的包装器,您的回答是关于原生 iOS api。
    猜你喜欢
    • 1970-01-01
    • 2018-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多