3.4 使用多个视图和控制器
到目前为止,只是给SampleViewController的ViewDidLoad方法添加了代码。这只是子类化UIViewController的几种方法之一。采用哪种方法取决于需要交互的视图控制器的视图处于生命周期中的哪一个阶段。视图控制器不会在实例化的时候就创建它的视图,因为视图都是延时并按需加载的,也就是说,只有在屏幕上显示的时候才加载,这样做的目的是为了减少资源消耗。虽然视图控制器通常都会在应用程序的整个生命周期内一直存在,但它的视图可能在浏览应用程序时多次重新创建。对于单一视图的应用程序,这并没有太大影响,但当应用程序是多控制器和多视图时,就会变得相当复杂,通过延迟并按需加载视图,就会对应用程序的性能和最大限度地减少资源利用造成很大影响。要在控制器内处理视图的生命周期,需要使用之前讲述过的UIViewController的视图生命周期管理方法。
为了说明这一点,下面创建一个带有两个单独视图控制器及其相关视图的示例。第一个视图控制器就是之前创建的SampleViewController。目前要做的是,通过代码创建第二个视图控制器及其视图。此前曾提过UITabBarController,这里将使用这个控件管理视图并处理它们之间的切换。
创建一个名为LMT3-3的新工程。因为要重用SampleViewController作为UITabBarController的起始控制器,所以要复制SampleViewController.xib、SampleViewCont.xib.cs和SampleViewController.xib.designer.cs文件到MonoDevelop中的新工程内,并修改其命名空间为LMT33。一旦将这些文件拖放到解决方案树中,MonoDevelop就会知道如何保持文件的正确关联。
在新工程的AppDelegate中,创建UITabBarController并给它添加多个控制器。创建UITabBarController并添加视图到窗口的过程与其他控制器的过程是一样的。UITabBarController可以创建一个有标签的界面实现通过选择标签切换视图。控制器自身管理所包含的视图控制器之间的切换。
为了将控制器添加到UITabBarController,只要将UITabBarController的ViewControllers属性值设置为UIViewController的数组即可。在数组内,添加SampleViewController的实例为第一项,然后简单地创建一个空的UIViewController作为数组第二项的占位符。每一个称为“标签栏条目”(Tab Bar Item)的标签条目,可以通过每个添加到UITabBarController控制器的TabBarItem属性进行设置。使用TabBarItem,可以设置标签条目的标题和图标,不过现在不设置图标。最终的代码如代码清单3-4所示。
代码清单3-4 添加UIViewController到UITabBarController
public partial class AppDelegate : UIApplicationDelegate
{
UITabBarController _tabController;
SampleViewController _sampleVC;
UIViewController _secondVC;
public override bool FinishedLaunching (UIApplication app,
NSDictionary options)
{
_tabController = new UITabBarController ();
_sampleVC = new SampleViewController ();
_sampleVC.TabBarItem.Title = "tab 1";
_secondVC = new UIViewController ();
_secondVC.TabBarItem.Title = "tab 2";
_tabController.ViewControllers =
new UIViewController[] { _sampleVC, _secondVC };
window.AddSubview (_tabController.View);
window.MakeKeyAndVisible ();
return true;
}
...
}
如果运行应用程序,第一个标签将会加载SampleViewController的视图,加速度计的数据将会像之前一样显示。如果切换到第二个标签,将会看到作为占位符添加的空白屏幕。切换回第一个标签,就会看到虽然切换到了第二个标签页,但加速度计的数据还在不断更新。当切换标签时,标签内的视图销毁与否,取决于它是否需要回收(例如,面对内存压力的时候),而当前示例不存在这个情况,所以视图还在继续更新数据。
提示 如果视图控制器接收到内存警告,并且它的视图没有在屏幕上显示,那么它就会卸载它的视图(当然,这也可通过重写默认配置实现)。当发生这种情况时,为了实现任何附加的清理代码,可在控制器子类的ViewDidUpload方法内实现。
如果要在视图没有显示时停止其活动,可以在控制器的ViewDidDisappear方法内处理。要停止接收加速度计的更新,最简单的方法是将委托设置为null。如果现在运行应用程序并在标签间来回切换,就会看到切换到第二个标签时,加速度计的数据并没有继续添加。然而,当切换回第一个标签时,数据也没有恢复添加。这是因为加速度计的实现代码是在ViewDidLoad方法内,而它只在视图第一次加载的时候才调用,而在视图显示或消失时,并不会调用它。为了实现每次视图显示时执行代码,可以在ViewDidAppear方法内实现。代码清单3-5列出了在SampleViewController的视图生命周期内实现更新的代码。如果现在运行应用程序,就可以看到当切换标签离开第一个视图时,加速度计就会停止记录更新,而切换回时就会恢复更新。
注意 也可以通过ViewWillAppear和ViewWillDisappear方法来实现之前在视图显示或消失时实现的效果。
代码清单3-5 SampleViewController中的视图生命周期方法
public partial class SampleViewController : UIViewController
{
MyAccelerometerDelegate _accelDelegate;
...
public override void ViewDidAppear (bool animated)
{
base.ViewDidAppear (animated);
UIAccelerometer accelerometer =
UIAccelerometer.SharedAccelerometer;
accelerometer.UpdateInterval = 0.25;
_accelDelegate = new MyAccelerometerDelegate (this);
accelerometer.Delegate = _accelDelegate;
}
public override void ViewDidDisappear (bool animated)
{
base.ViewDidDisappear (animated);
UIAccelerometer accelerometer =
UIAccelerometer.SharedAccelerometer;
accelerometer.Delegate = null;
}
class MyAccelerometerDelegate : UIAccelerometerDelegate
{
...
}
}
现在将注意力转移到第二个视图控制器及其视图。在工程中添加两个新类,一个名为SecondViewController,另一个为SecondView。SecondViewController是控制器,所以设置它的基类为UIViewController,并添加MonoTouch.UIKit命名空间。当控制器第一次加载它的视图时,将调用它的LoadView方法。之前在xib文件中定义的控制器会加载视图,因为LoadView的实现默认情况下会从xib文件读取视图。然而,为了通过代码创建控制器的视图,可以重写LoadView方法。在SecondViewController类内,添加LoadView的实现,并在实现内创建SecondView的实例,然后将它关联到控制器。在实现SecondView的同时,可以设置视图的属性,如背景颜色,代码如下:
public class SecondViewController : UIViewController
{
public SecondViewController ()
{
}
public override void LoadView ()
{
base.LoadView ();
this.View = new SecondView();
this.View.BackgroundColor = UIColor.White;
}
}