当nib加载完毕后,其实例已经是功能完备的了;它们已经通过属性与尺寸查看器中的所有属性初始化和配置好了,其插座变量用于设置相应实例变量的值。然而,当对象从加载的nib中实例化后,你可能还想向初始化过程附加自己的代码。本节将会介绍几种做法。
一种常见的情况是视图控制器(当包含主视图的nib加载时,它作为拥有者,因此在nib中表示为nib拥有者对象)拥有一个插座变量,它指向从nib实例化的界面对象。在这种架构中,视图控制器可以对该界面对象做进一步的配置,因为nib加载后它有一个指向它的引用——相应的实例属性。进行这种配置最方便的地方就是其viewDidLoad方法。在调用viewDidLoad时,视图控制器的视图已经加载了,也就是说,视图控制器的view属性已经设为了实际的主视图,这是从nib实例化的,所有插座变量都会连接起来;不过视图尚未出现在可视化界面上。
另一种可能是除了在nib中进行配置,你还希望nib对象能够自我配置。通常来说,这是因为你有一个内建界面对象类的自定义子类。事实上,你想要创建自定义类,从而放置一些自定义代码。你要解决的问题是nib编辑器不允许你进行后续配置,或有很多对象,并且希望以一种一致且精心设定好的方式进行配置,这样相对于单独配置每一个,通过共同类进行配置才更有意义。
一种方式是在自定义类中实现awakeFromNib。对象通过nib加载实例化后(对象初始化并配置完毕,其连接也建立起来了),awakeFromNib会向所有这些对象发送消息。
比如,下面创建一个按钮,无论在nib中如何配置,其背景色总是红色的(这个例子没什么意义,不过能说明问题)。在Empty Window项目中,创建一个按钮子类RedButton:
1.在项目导航器中,选择File→New→File。然后选择iOS→Source→Cocoa Touch Class,单击Next按钮。
2.将新建的类命名为RedButton,让它成为UIButton的子类,单击Next按钮。
3.确保将其保存到项目目录中,位于Empty Window分组下,同时勾选Empty Window应用目标,单击Create按钮。Xcode会创建RedButton.swift。
4.在RedButton.swift的RedButton类的声明中,实现awakeFromNib:
override func awakeFromNib { super.awakeFromNib self.backgroundColor = UIColor.redColor}
现在有一个UIButton子类,在其从nib实例化时,它会变成红色。不过,任何nib中都没有该子类的实例。编辑故事板,选中已经位于主视图中的按钮,使用身份查看器将按钮所属的类改为RedButton。
构建并运行项目。当然,按钮现在是红色的!
还可以使用nib对象身份查看器中的User Defined Runtime Attributes。可以通过它配置nib编辑器中没有内建界面的nib对象的方方面面。这里实际做的是在nib加载时发送nib对象,一个setValue:forKeyPath:消息,键路径会在第10章介绍。自然而然地,对象需要做一些准备来响应给定的键路径,否则nib加载时应用会崩溃。
比如,nib编辑器的一个缺点是它无法配置层属性。假设我们要通过nib编辑器将红色按钮改为圆角的。在代码中,要通过设置按钮的layer.cornerRadius属性实现上述目标。nib编辑器无法访问该属性。相反,可以在nib编辑器中选中按钮,使用身份查看器中的User Defined Runtime Attributes。将Key Path设为layer.cornerRadius,将Type为Number,将Value值设置成什么都可以,如10(如图7-17所示)。现在构建并运行;当然,按钮现在变成圆角的了。
还可以通过将属性设为可检查的来配置nib对象的自定义属性。为了做到这一点,请将@IBInspectable特性添加到属性声明中。这样,属性就会列在nib对象的属性查看器中。比如,现在准备在nib编辑器中配置按钮的边框。在RedButton类声明体的开头添加如下代码:
@IBInspectable var borderWidth : CGFloat { get { return self.layer.borderWidth } set { self.layer.borderWidth = newValue }}
图7-17:通过运行时特性将按钮修改为圆角
上述代码声明了一个RedButton属性borderWidth,并使其成为层的borderWidth属性前的一个门面。这样,如果一个按钮是RedButton类的实例,那么nib编辑器还会在属性查看器中显示出该属性(如图7-18所示)。结果就是,当在nib编辑器中为该属性赋值时,这个值会在nib加载时发送给该属性的setter,按钮边框就会变成值所指定的宽度。
图7-18:nib编辑器中可检查的属性
要想更早地介入对象的初始化过程中,如果对象是个UIView(或是UIView的子类),那么可以实现init(coder):。值得注意的是,对于UIView,nib加载实例化时并不会调用init(frame:),而会调用init(coder:)。(实现init(frame:),然后想知道当视图从nib实例化时为何代码不会被调用是初学者常犯的一个错误)。其最简单的实现如以下代码所示:
required init?(coder aDecoder: NSCoder) { super.init(coder:aDecoder) // your code here}