[Xamarin]删除Android及iOS控制端堆栈

来源:互联网 发布:淘宝钻级店铺转让 编辑:程序博客网 时间:2024/06/02 02:26

Xamarin开发跨平台App新人一名。

最近组里Xamarin的项目中的一个要求,假设有 A->B->C->D 这样经由控制端跳页的流程,跳到D之后按返回键时希望能够直接返回到A。

做法就是跳到D之后删除之前在控制端堆栈中删除到A为止的控制端。

Android和iOS原生的实现代码已经有不少了,参考一些之后再这里贴一下Xamarin中C#的实现代码。


Android端:

我主要参考了以下这篇文章的Java代码

http://www.jianshu.com/p/263d40582a08

思路就是把所有的Activity(控制端类)抽象出一个基础Activity,让所有的Activity继承这个CommonActivity。

利用List以及多态性做成一个管理所有Activity的类ActivityCollector,然后添加一些需要的方法比如AddActivity,RemoveActivity。

public abstract class CommonActivity : Activity{    // 抽象控制基类}

 public class ActivityCollector    {        private static List<CommonActivity> activities = new List<CommonActivity>();        public static List<CommonActivity> Activities        {            get { return activities; }        }        public static void AddActivity(CommonActivity activity)        {            activities.Add(activity);        }        public static void RemoveActivity(CommonActivity activity)        {            activities.Remove(activity);        }        public static void FinishAll()        {            foreach(CommonActivity activity in activities)            {                if (!activity.IsFinishing)                {                    activity.Finish();                }            }        }    }

那么如何利用ActivityCollector来管理控制堆栈呢?

简单地说就是要结合AndroidApp生命周期,在某个Activity被创建或是被销毁时同步更新ActivityCollector中的内容,

这就需要在CommonActivity的OnCreate和OnDestroy中添加一些代码

public abstract class CommonActivity : Activity {    protected override void OnCreate(Bundle bundle)    {        base.OnCreate(bundle);        ActivityCollector.AddActivity(this);    }    protected override void OnDestroy()    {        base.OnDestroy();        ActivityCollector.RemoveActivity(this);    }}

回到项目要求。在页面D生成时,删除堆栈中直到页面A的所有Activity,就能在D页面按下返回键时直接返回到A页面。

如果有必要可能需要加一些判断语句判断是不是从C页面跳转到D页面防止误删。

public class ActivityOfD : Activity{    protected override void OnCreate(Bundle bundle)    {        base.OnCreate(bundle);                // 可以根据需要添加一些判断语句            for (int i = ActivityCollector.Activities.Count - 2; i >= 0; i--)            {                if ( !(ActivityCollector.Activities[i] is ActivityOfA) )                {                    ActivityCollector.Activities[i].Finish();                }            }        }}


iOS端:

iOS端的做法和Android端不太一样。iOS的SDK已经提供了控制端堆栈管理API:UINavigationController类,而Xamarin也已经帮我们包装成了C#类,可以直接调用。

我主要参考这篇文章的部分代码。

http://www.jianshu.com/p/90a104ac6633

C#中无法将NSMutableArray类强制转换成ViewController数组(应该说C#的默认数组都是继承自Array类而不是NSArray类),因此我们用List来处理,代码如下。

public class ViewControllerOfD : CommonViewController{    public override void ViewWillAppear (bool animated)    {        base.ViewWillAppear (animated);        // 可以根据需要添加一些判断语句            List<UIViewController> controllers = new List<UIViewController> (NavigationController.ViewControllers);            for (int i = controllers.Count - 2; i >= 0; i--)            {        if( !(controllers[i] is ViewControllerOfA))                {                    controllers.RemoveAt(i);                }            }    NavigationController.SetViewControllers(controllers.ToArray(), true);    }}


但是一开始时我在重新设置ViewController时用的是以下一行代码:

NavigationControler.ViewControllers = controllers.ToArray();

这样做产生了一个问题,虽然画面可以按照预想在页面D按back键时直接跳转回页面A,但屏幕上方的导航栏却依然还是退到页面C的导航栏,需要多次按下back键才能回退到页面A的导航栏。

百思不得其解之时得到了前辈的指点,我查阅了iOS的官方开发者文档

https://developer.apple.com/library/content/documentation/WindowsViews/Conceptual/ViewControllerCatalog/Chapters/NavigationControllers.html

以及UINavigationController类的文档

https://developer.apple.com/documentation/uikit/uinavigationcontroller


如上官方示意图表示了NavigationController的构成。原来ViewControllers的堆栈与NavigationBar(导航栏)是NavigationController下不同的成员。

所以只去改掉ViewControllers的话当然不会影响到导航栏。但当我试图直接改变UINavigationBar.Item时,又会在运行中报出“Cannot set items managed by viewController”的异常。

再一查文档发现原来就如字面意思iOS SDK规定不允许直接修改ViewController管理下的导航栏控件。

那么该怎么办呢?

其实很简单,NavigationController里提供了设置ViewController堆栈的方法SetViewController,该方法会自动帮我们一起设置好相应的导航栏堆栈。最终的代码如之前所示。

iOS端的问题卡了有半天才解决,感觉自己还是对iOS原生应用开发不熟悉,还需要多积累经验和多读官方文档。


原创粉丝点击