UIScreen是与设备有关的物理屏幕
Window是一个窗口对应UIWindow类,继承自UIView,window要显示在Screen上必须设置为主窗口并且可见。接下来就可以往UIWindow上面添加一些控件了。
下图就是简单地层次关系
ViewController是用来组织和控制视图的,与上图不同的是这里使用了视图控制器ViewController,不需要直接将view指定给window,相反,只需要给window制定一个视图控制器,视图控制器会自动的将他的view添加给window。如下图所示:
引入控制器有点像Android里面的Activity。
UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; levelViewController = [[LevelViewController alloc] init]; window.rootViewController = levelViewController; // 类似于下面这句 //[window addSubView: levelViewController.view]; [window makeKeyAndVisible];
iOS会自动的执行下面的操作
实例化一个window
加载主storyboard并且实例化初始ViewController
指定视图控制器为window的根控制器rootViewController,接下来使得window显示在屏幕上
使用代码描述大概:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; QHViewController * vc = [storyboard instantiateInitialViewController]; self.window.rootViewController = vc; [self.window makeKeyAndVisible]; return YES; }
注意:以上三个步骤是iOS主动做的,因此建立singleView的项目会看不到application:didFinishLaunchingWithOptions:方法里面有任何内容。
要想自己使用storyboard里面的其他控制器需要通过UIStoryBoard对象来实现
a. 通过UIStoryboard类的storyboardWithName方法得到UIStoryBoard对象
b. 通过instantiateInitialViewController或者instantiateViewControllerWithIdentifier:方法得到对应的控制器
代码大致如下:
UIStoryboard *storyboard = self.storyboard; SpecialViewController *svc = [storyboard instantiateViewControllerWithIdentifier:@"SpecialViewController"]; // Configure the new view controller here. [self presentViewController:svc animated:YES completion:nil];
Xib是storyboard的前身,使用storyboard的好处是可以建立起界面跳转的关系,看起来更加直观。
创建一个继承自UIViewController的类。创建一个xib文件,将其File’s owner指定为自定义的控制器,并且一定要将view属性连线到要显示的那个View里面(因为一个xib文件里面可能有多个UIView)
QHViewController *controller=[[ QHViewController alloc]initWithNibName:@"test" bundle:nil];
其实上面这句若写成了
QHViewController *controller=[[ QHViewController alloc]init];
也可以加载xib,只不过加载不了名字为text.xib的文件,后面会讲到。
可能的错误:
a. was unable to load a nib named XXX:说明xib里面没有任何view
b. loaded the XXX nib but the view outlet was not set :将其File’sowner指定为自定义的控制器,并且一定要将view属性连线到要显示的那个View里面
- (void)applicationDidFinishLaunching:(UIApplication *)application { UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; levelViewController = [[LevelViewController alloc] init]; window.rootViewController = levelViewController; [window makeKeyAndVisible]; }
上面这句levelViewController = [[LevelViewController alloc] init];在创建自己的控制器的时候其实会做以下的处理(控制器里面的view属性):
a.默认会查找与控制器匹配的.xib文件,匹配的意思是如果该控制器类为QHFirstViewController,那么匹配的.xib文件为QHFirstView.xib
b.如果未找到,会查找和控制器同名的.xib文件
c.以上均未找到,就会创建一个空白的View将其赋值给控制器的view属性。
self.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
上面的c步骤可以通过在viewDidLoad方法里面打印self.view是有值的看出来,创建该view是在loadView方法里面做的。当view需要被展示而它却是nil时,viewController会调用该方法。可以重写一下loadView方法,发现程序停在那里无法执行,原因就是父类会做加载view的操作,调用[super loadView]就可以。使用纯代码写布局的话就不需要调用[super loadView]了,因为该方法的父类中实现的无外乎是通过storyboard或者xib加载view,既然要使用代码自定义也就没必要从其他地方加载了。
事实上,通过storyboard,xib也是在loadView方法里面加载的。loadView方法做的事情如下图所示
官方给的图相当的好,展示出了loadView方法所做的事,只是通过xib加载view没有展现在上面,并且官方的文档没有xib相关的内容取而代之的是storyboard,这与苹果推崇storyboard是一致的。
控制器加载View的步骤如下:
如果view在内存中,则直接加载。相反,如果不存在,则UIViewController调用loadView方法
loadView方法执行如下操作:
1.如果你重载了这个方法,则必须创建必要的view并且将一个非nil值传给UIViewController的view属性。
2.如果你没有重载这个函数,UIViewController会默认使用UIViewController的nibName和nibBundle属性尝试从nib文件加载view。如果没有找到nib文件,则ViewController会通过以下两个步骤找到与其关联的nib。
A 如果类名包含Controller,例如ViewController的类名是MyViewController,则查找是否存在MyView.nib;
B 找跟ViewController类名一样的文件,例如MyViewController,则查找是否存在MyViewController.nib。
如果没有可用的nib文件,那么它创建一个空的UIView作为它的view。
控制器被设为主storyboard的初始控制器,才会先去storyboard查找view,否则不会去storyboard查找。