7视图控制对象

视图控制对象ViewControllers

UIViewController的作用是管理应用的屏幕,一个UIViewController对象负责一个屏幕。UIViewController有一个名为view的属性,可以指向某个UIView对象或者UIView子类的对象,这个UIView对象就是UIViewController对象负责管理的屏幕。通常情况下UIView对象是全屏尺寸的视图。View属性所指向的视图也可以有子视图,所以UIViewController对象可以管理一个视图层次结构。因为view是这个层次结构的根视图,所以当程序将某个视图控制对象的view作为子视图加入窗口时,也会加入整个视图层次结构。

UIViewController的loadView方法会创建一个UIView对象,并将新创建的实例赋给其view属性。UIViewController的子类可以覆盖UIViewController的loadView方法,然后在该方法中创建自己的视图层次结构。


深入浅出XIB:File’s Owner

XIB文件包含的是对象:在Xcode中将某个对象拖拽至画布,就会将该对象存入相应的XIB文件。当应用载入某个XIB文件后,会将该XIB文件所包含的对象重新载入内存。凡是XIB文件已经保存的对象都会出现在Objects栏中。XIB文件的这种对象保存机制称为固化archive。因此Objects栏中的对象也称为已固化对象(现在xcode中已经没有Objects栏目了)。

XIB文件还包含另一类对象:占位对象placeholder object。

在大纲视图的placeholder栏下有两个对象:File’s Owner和First Responder

XIB为什么需要File’s Owner这个占位对象:

在某个视图控制器对象载入视图后,会将其view属性指向该视图以便在将来需要时引用(例如将视图加入窗口并显示)。

载入视图可以通过代码实现,也就是通过loadView方法中实现载入视图的代码,让代码在编译时刻完成视图的准备工作。

另一种是通过载入特定的xib文件来创建视图,载入xib文件时,应用会解固unarchiveXIB文件中的所有已固化对象,随之而来的问题是:应该将哪个视图赋给Controller的View属性,File’s Owner就是为了解决上诉问题准备的。点击后进入标识检视面板填写controller名称。

XIB文件中针对插座变量所建立的关联,等同于”根据插座变量的变量名,向包含该插座变量的对象发送相应的存消息。(例如:如果有一个XIB文件,该文件固化了一个对象,该对象有一个名为View的插座变量,并且已经和某个视图建立了关联。当应用载入这个XIB文件时,就会向这个对象发送setView:消息。传入的实参是位于view关联另一端的对象)。

小结

视图控制器对象的作用是管理屏幕,一个视图控制对象对应一个屏幕,而这个屏幕其实是一个视图。视图控制对象的视图可以有子视图,从而构建出一个视图层次结构。视图控制对象可以通过两种不同的途径创建其视图。

如果视图控制对象的视图没有任何子视图,就可以选择使用编写代码的途径来创建视图。具体做法是:先覆盖loadView方法,然后创建所需的视图,最后将这个新创建的视图通过setView:消息赋给view属性。

如果视图控制对象的视图有子视图,就可以选择使用XIB文件的途径来创建视图。具体做法是:先创建一个XIB文件,然后加入所需的视图层次结构,最后将File’s Owner的View插座变量关联至XIB文件中的顶层视图。

UITabBarController

UITabBarController可以保存一组UIViewController对象。此外,UITabBarController对象还会在屏幕底部显示一个标签条tab bar,标签条会有多个标签项tab,分别对应UITabBarController对象所保存的每一个UIViewController对象。单击某个标签项,UITabBarController对象就会显示该标签项所对应的UIViewController对象的视图。

将UITabBarController对象设置为UIWindow实例的rootViewController。这意味着UITabBarController自己也是UIViewController子类,也有一个名为View的属性。UITabBarController对象的view指向一个空白的视图,该视图包含两个子视图:标签条河当前选中的视图控制对象的视图。标签条上的标签可以显示标题和图片。具体数据需要由视图对象的tabBarItem属性提供,当UITabBarController对象加入一个视图控制对象时,就会为标签条增加一个标签项,并根据新加入的视图控制对象的tabBarItem属性设置该标签项的标题和图片。KeypadViewController-tabBarItem(UITabBarItem-title+image)-Keypad


视图控制对象的生命周期

视图控制对象负责维护其下的视图,需要完成的任务依次包括:创建视图、显示视图、隐藏视图、释放视图。视图控制对象可能需要多次执行这一系列任务。以上这些构成视图控制对象的生命周期。

初始化视图控制对象

UIViewController的指定初始化方法是initWithNibName:bundle:。该方法包含两个实参,分别用于指定视图控制对象的XIB文件的文件名和该XIB文件所在的程序包(这里的程序包就是Xcode在创建iOS应用类型的目标时创建的相应的程序包)

调用initWithNibName:bundle时,如果两个参数都传入nil,那么UIViewController对象会在需要载入视图时执行操作:在应用程序包中,查找当前的UIViewController类同名的Xib文件。此外也可以在调用initWithNibName:bundle:时明确指定文件和程序包。

在实际开发中,可以为UIViewController子类和该子类需要载入的xib文件取相同的名称。这样在初始化视图对象时,只需要发送init消息即可。

UIViewController与延迟载入

视图控制对象不会在其被创建出来的那一刻马上创建并载入相应的视图。只有当应用需要将某个视图控制对象的视图放上屏幕时,相应的视图控制对象才会创建其视图。这种延迟创建视图的做法能提高内存的使用率。

每个UIViewController对象都实现了viewDidLoad方法,该对象会在载入视图后立刻调用该方法。

在iOS6以前,如果点击Hardware菜单中的simulate Memory warning菜单项会释放没有在当前屏幕显示的视图控制对象的视图。iOS6以后,就不会释放view了,会释放resources清理当前不用的资源。

当前用户选择标签项时,UITabBarController对象会尝试获取控制器对象的视图,以便将得到的视图作为子视图加入自己的视图。控制器对象会在收到view消息但是实例变量view尚未赋值的情况下,自动调用loadView方法。

通过XIB文件载入视图的UIViewController对象,会有完全相同的延迟载入机制。事实上,UIViewController的loadView方法会通过initWithNibName:bundle:方法传入的信息,载入特定的XIB文件。所以当某个UIViewController子类为了通过代码载入视图而覆盖loadView方法时,通常不会调用父类的loadView方法。因为一旦调用了UIViewController的loadView方法,就会执行查找并载入XIB文件的代码。因为视图控制对象可以释放其视图并重新载入,所以在为视图控制对象编写初始化代码时,要注意不要编写和view属性有关的代码。

!规则:在initWithNibName:bundle:中为某个UIViewController子类实现初始化代码时,不应该访问view或view的任何子视图。凡是view和view的子视图有关的初始化代码,都应该在viewDidLoad方法中实现。

卸载视图

视图的显示和消失

- (void)viewWillAppear:(BOOL)animated //即将加入窗口

- (void)viewDidAppear:(BOOL)animated //已经加入窗口

- (void)viewWillDisappear:(BOOL)animated// 即将消失、被覆盖或者隐藏

-(void)viewDidDisappear:(BOOL)animated //已经消失、被覆盖或者隐藏

好处:如果某个应用使用了会占用大量内存的数据结构,而这些数据结构只有在应用正在显示某个视图控制对象的视图时才会用到,那么该应用就可以在隐藏视图时,释放相应的数据结构。上述四个方法的实现是空的。目的是让UIViewController的子类可以覆盖这些方法。

要点:视图控制对象和其视图是各自独立的两个对象。视图控制对象需要借助某视图才能和用户交互。

main 函数与UIApplication

int main(int argc, char * argv[]) {

    @autoreleasepool {

        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));

    }

}

UIApplicationMain函数会创建一个UIApplication实例。没有iOS应用都会有且只有一个UIApplication实例,该实例的作用是维护运行循环。一旦创建了某个UIApplication实例,该实例的运行循环就会一直循环下去,main函数的执行也会因此阻塞。

此外,UIApplicationMain函数还会创建某个指定类的实例,并将其设置为UIApplication实例的delegate。该实例的类是由UIApplicationMain函数的最后一个实参指定的,该实参的类型是NSString对象,代表的是某个类的类名。所以在以上这段代码中,UIApplicationMain会创建一个AppDelegate实例,并将其设置为UIApplication实例的delegate。

在应用启动运行循环并开始接收事件前,UIApplication实例会向其委托对象发送一个特定消息,使应用能有机会完成相应的初始化工作。。这个消息的名称是application:didFinishLaunchingWithOptions:。

retina显示屏

对于非retina显示屏,一个点是一个像素。对于retina显示屏,1个点是2像素*2像素

模糊的解决方案是:在应用程序包里放两套图:一套针对非Retina显示屏,像素分辨率和相应的屏幕点数相同:另一套针对Retina显示屏,像素尺寸比前一套大一倍。

所以程序猿唯一要做的是为高分辨率的图片文件加上后缀@2x。



posted @ 2015-09-28 18:28  captivity  阅读(107)  评论(0)    收藏  举报