从File菜单中选择New→File…,然后选中窗口左侧iOS部分的Cocoa Touch,再选中窗口右侧的Objective-C class,最后单击Next按钮。
在新出现的面板中,在Class文本框中输入BNRHypnosisViewController,在Subclass of下拉菜单中选择NSObject,单击Next按钮(见图6-4)。Xcode会提示创建文件,单击Create按钮。
图6-4 创建BNRHypnosisViewController
读者可能会问,为什么要将BNRHypnosisViewController的父类设置为NSObject,而不是UIViewController?选择NSObject会生成最简单的Xcode文件模板,从最简单的模板开始编写代码有利于读者理解应用的工作原理。
打开BNRHypnosisViewController.h,将BNRHypnosisViewController的父类修改为UIViewController,修改后的代码如下:
@interface BNRHypnosisViewController : NSObject
@interface BNRHypnosisViewController : UIViewController
@end
UIViewController的view属性
BNRHypnosisViewController从UIViewController中继承了一个重要属性:
@property (nonatomic, strong) UIView *view;
view属性指向一个UIView对象。之前介绍过,UIViewController对象可以管理一个视图层次结构,view就是这个视图层次结构的根视图,当程序将view作为子视图加入窗口时,也会加入UIViewController对象所管理的整个视图层次结构(见图6-5)。
图6-5 HypnoNerd的对象图
视图控制器不会在其被创建出来的那一刻马上创建并载入相应的视图。只有当应用需要将某个视图控制器的视图显示到屏幕上时,相应的视图控制器才会创建其视图。这种延迟加载(lazy loading)视图的做法能提高内存的使用效率。
视图控制器可以通过两种方式创建视图层次结构。
•代码方式:覆盖UIViewController中的loadView方法。
•文件方式:使用Interface Builder创建一个NIB文件,然后加入所需的视图层次结构,最后视图控制器会在运行时加载由该NIB文件编译而成的XIB文件。(在第1章中介绍过,应用在运行时会按需载入XIB文件并激活文件中的视图。)
因为BNRHypnosisViewController的视图层次结构中只包含一个视图,所以接下来将通过代码方式来创建视图。
通过代码方式创建视图
在BNRHypnosisViewController.m顶部导入BNRHypnosisView的头文件,然后覆盖loadView方法,创建一个大小与屏幕相同的BNRHypnosisView对象并将其赋给视图控制器的view属性:
#import /"BNRHypnosisViewController.h/"
#import /"BNRHypnosisView.h/"
@implementation BNRHypnosisViewController
- (void)loadView
{
// 创建一个BNRHypnosisView对象
BNRHypnosisView *backgroundView = [[BNRHypnosisView alloc] init];
// 将BNRHypnosisView对象赋给视图控制器的view属性
self.view = backgroundView;
}
@end
视图控制器刚被创建时,其view属性会被初始化为nil。之后,当应用需要将该视图控制器的视图显示到屏幕上时,如果view属性是nil,就会自动调用loadView方法。
接下来将BNRHypnosisViewController对象的视图层次结构加入应用窗口,并将其显示在屏幕上。
设置根视图控制器
为了将视图控制器的视图层次结构加入应用窗口,UIWindow对象提供了一个方法:setRootViewController:。当程序将某个视图控制器设置为UIWindow对象的rootViewController时,UIWindow对象会将该视图控制器的view作为子视图加入窗口。此外,还会自动调整view的大小,将其设置为与窗口的大小相同。
首先在BNRAppDelegate.m文件顶部导入BNRHypnosisViewController.h,然后创建一个BNRHypnosisViewController对象,最后将其设置为UIWindow对象的rootViewController,代码如下:
#import /"BNRAppDelegate.h/"
#import /"BNRHypnosisViewController.h/"
@implementation BNRAppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:
[[UIScreen mainScreen] bounds]];
// 在这里添加应用启动后的初始化代码
BNRHypnosisViewController *hvc = [[BNRHypnosisViewController alloc] init];
self.window.rootViewController = hvc;
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
rootViewController的view需要在应用启动完毕后就显示,所以UIWindow对象会在设置完rootViewController后立刻加载其view。
假设读者需要自己实现setRootViewController:,那么可以根据第4章所学到的知识,代码示例如下:
- (void)setRootViewController:(UIViewController *)viewController
{
// 获取根视图控制器的视图
UIView *rootView = viewController.view;
// 根据UIWindow对象的bounds,为视图创建相应的frame
CGRect viewFrame = self.bounds;
rootView.frame = viewFrame;
// 将视图作为子视图加入UIWindow对象
[self addSubview:rootView];
// 将viewController赋给实例变量_rootViewController
_rootViewController = viewController;
}
方法中的第一行代码需要使用根视图控制器的view,由于此时根视图控制器刚刚被创建,其view是nil,因此需要向其发送loadView消息创建view。
构建并运行应用,HypnoNerd的界面应该与Hypnosister的类似,但是HypnoNerd是使用视图控制器来显示BNRHypnosisView的,而不是直接将BNRHypnosisView添加到UIWindow。虽然使用视图控制器增加了复杂性,但是在本章结尾就能看到这样做的灵活性和可扩展性。