首页 » iOS编程基础:Swift、Xcode和Cocoa入门指南 » iOS编程基础:Swift、Xcode和Cocoa入门指南全文在线阅读

《iOS编程基础:Swift、Xcode和Cocoa入门指南》7.2 nib加载

关灯直达底部

nib文件是个关于潜在实例的集合,这些实例就是其nib对象。当应用运行并加载nib时,这些实例才会创建出来。这时,包含在nib中的nib对象会转换为应用可以使用的实例。

这种架构颇具效率。nib通常会包含界面;界面是相对重量级的对象。nib只在需要时才会加载;实际上,它可能永远都不会加载。通过这种方式,我们可以确保内存使用量最低,而这是非常重要的,因为移动设备上的内存是非常昂贵的资源。此外,加载nib是需要时间的,因此启动时加载少量nib(足够生成应用的初始界面即可)会加快启动速度。

并没有所谓的“卸载”nib。nib加载过程所完成的事情是生成一些实例;当这些实例生成后,nib的工作就完成了。此后将会由运行着的应用来决定该如何使用生成的这些实例。只要需要这些实例,应用就得保持对其的引用;如果不再需要,那就可以将其销毁。

可以将nib文件看作用于生成实例的一组指令;当nib加载时会执行这些指令。相同的nib文件可以加载多次,每次都生成一组新的指令。比如,一个nib文件可能包含应用中多处都会用到的一些界面。代表表格中一行的nib文件可能会加载多次,从而生成表格中的多行。

7.2.1 何时加载nib

当应用运行时,有一些主要的场景通常会加载nib文件:

从故事板中实例化视图控制器

故事板是场景的集合。每个场景都从一个视图控制器开始。当需要该视图控制器时,它会通过故事板实例化出来。这意味着包含该视图控制器的nib会加载进来。

视图控制器会通过故事板自动实例化出来。比如,当应用启动时,如果有主故事板,那么运行时就会寻找该故事板的初始化视图控制器(入口点)并将其实例化(参见第6章)。与之类似,故事板常常会包含由Segue连接的几个场景;在执行Segue时,目标场景的视图控制器会被实例化出来。

还可以在代码中以手工方式从故事板中实例化视图控制器。要想做到这一点,请从一个UIStoryboard实例开始,然后:

·可以通过调用instantiateInitialViewController来实例化故事板的初始视图控制器。

·可以通过调用instantiateViewControllerWithIdentifier:来实例化视图控制器,前提是该视图控制器的场景是在故事板中通过标识符字符串来命名的。

视图控制器从nib中加载主视图

视图控制器有一个主视图。不过视图控制器是个轻量级对象(只包含少量代码),而主视图则是个相对重量级的对象。因此,在实例化时,视图控制器会缺少主视图。当需要将视图放到界面中时,它才会将主视图生成出来。视图控制器可以通过几种方式来包含主视图;其中一种方式就是从nib中加载主视图。

如果视图控制器属于故事板中的某个场景,并且在故事板的画布中包含了视图(通常来说均如此),就像Empty Window示例项目一样,这就会涉及两个nib:包含视图控制器的nib与包含主视图的nib。包含视图控制器的nib会加载进来以便实例化视图控制器,就像之前所介绍的那样;现在,当该视图控制器实例包含了主视图时,主视图nib会自动加载,连接到该视图控制器的整个界面就会创建出来。

如果视图控制器是通过其他方式实例化的,那就会有一个与之相关的.xibgenerated nib文件,其中包含了主视图。重申一次,视图控制器会自动加载这个nib,然后在需要时抽取出主视图。这种视图控制器与主视图nib文件之间的关联是通过nib文件名来实现的。第6章曾通过UIViewController初始化器init(nibName:bundle:)以代码的方式配置这种关联,如下所示:


self.window!.rootViewController =    MyViewController(nibName:/"MyViewController/", bundle:nil)  

上述代码会让视图控制器将自己的nibName属性设为/"MyViewController/"。这意味着当视图控制器需要其视图时,它是通过加载来自于MyViewController.xib的nib实现的。

代码显式加载nib文件

如果nib文件来自于.xib文件,那么代码可以手工加载它,这是通过调用如下方法之一来做到的:

loadNibNamed:owner:options:

这是个NSBundle实例方法。通常,你会直接调用NSBundle.mainBundle()。

instantiateWithOwner:options:

这是个UINib实例方法。在实例化UINib并通过init(nibName:bundle:)对其初始化时会确定好nib。

应用运行时指定nib文件实际上需要两部分信息:其名字与包含它的包。视图控制器不仅有nibName属性,还有一个nibBundle属性,用于指定nib的方法,如init(nibName:bundle:),会有一个bundle:参数。不过实际上,这个包都是应用包(或NSBundle.mainBundle(),它们是一回事);这是默认的,因此无须再指定包了。可以直接传递一个nil,不必再显式提供一个包了。

7.2.2 手工加载nib

在实际情况下,你会将应用配置为会自动加载大多数nib,这与刚才提到的各种机制与场景相一致。不过为了理解nib加载过程,手工加载nib也是非常有益的,下面就来实现。

首先在Empty Window项目中创建并配置一个.xib文件:

1.在Empty Window项目中,选择File→New→File并指定iOS→User Interface→View。这是个.xib文件,包含了一个UIView实例。单击Next按钮。

2.在Save对话框中,对新的.xib文件保持默认名字View,单击Create按钮。

3.现在回到项目导航器中;View.xib文件已经创建出来并被选中,可以在编辑器中查看其内容。这些内容包含了一个UIView。它非常大,因此请将其选中,在属性查看器中,在Simulated Metric下,将Size弹出菜单修改为Freeform。这时,处理器会出现在画布中的视图旁边;拖曳它将视图缩小。

4.使用任意子视图来装配视图,方式是将它们从对象库中拖曳到视图上。还可以配置视图本身;比如,在属性查看器中修改背景色(如图7-7所示)。

图7-7:在.xib文件中设计视图

现在的目标是当应用运行时在代码中手工加载这个nib文件。编辑ViewController.swift,在viewDidLoad方法体中,插入下面这行代码:


NSBundle.mainBundle.loadNibNamed(/"View/", owner: nil, options: nil)  

构建并运行应用。发生了什么?来自于View.xib的设计好的视图去哪儿了?nib加载失败了吗?

不是。nib加载没有失败。它已经加载了!不过,我们还差两步。记住,在加载nib时要执行3个任务。

1.加载nib。

2.加载时获取它所创建的实例。

3.对实例进行一些处理。

我们执行了第1个任务(加载nib),但并没有从中获取实例。这样,实例虽然创建出来,但随后又烟消云散了。为了防止这种情况的发生,我们需要通过某种方式捕获到这些实例。对loadNibNamed:owner:options:的调用会返回一个顶层nib对象数组,这些nib对象是从nib加载过程中实例化的。这就是我们需要捕获的实例!我们只有一个顶层nib对象(UIView),因此捕获数组的第1个元素(也只有这唯一一个元素)即可。重写代码如下所示:


let arr = NSBundle.mainBundle.loadNibNamed(/"View/", owner: nil, options: nil)let v = arr[0] as! UIView  

现在执行了第2个任务:捕获到加载nib时所创建的实例。现在,变量v会引用全新的UIView实例。

不过,在构建并运行应用时,依然什么都不会出现,这是因为我们并未对该UIView进行任何处理。这是第3个任务。下面就对该UIView进行一些处理:将其放到界面上!再次重写代码,如下所示:


let arr = NSBundle.mainBundle.loadNibNamed(/"View/", owner: nil, options: nil)let v = arr[0] as! UIViewself.view.addSubview(v)  

构建并运行应用,视图终于出现了!这证明了nib加载如我们所愿:我们可以在运行着的应用界面上看到在nib中所设计的视图(如图7-8所示)。

图7-8:nib加载的视图出现在了界面上