IOS 的loadView 及使用loadView中初始化View注意的问题。(死循环并不可怕)
在XCode 4.2后,我基本上的应用都不使用Xib文件了,虽然xib文件有很多好趣,可以快速免代码构建视窗,可以减少好多代码构建带来的麻烦,其实能用xib还是不错的,主要是我的机器打开xib来编辑时太慢了,跑不动了,老古董 的机器了,所以不怎么喜欢xib了(个人原因)。有人说xib会让代码跑起来效率慢,真的是这样吗?从理论上来看,APP要运行,先读INFO.PLIST文件,然后找到MAINWINDOW 的XIB,然后解释XIB中的代码来演变成OC代码进行实例化。而用普通代码构建,直接使用代码CODE而不用翻译XIB中的数据,省了一个步骤,或许就是这相原因吧。好了,这个先别讨论了,回到loadView,每一个VC(ViewController)都会生成一个loadview方法,当然,很多情况下都不怎么在这个方法中来写视窗,而是在ViewDidLoad中来写。先看看loadView调用的触发条件吧。
APP 运行,先跑init 然后跑
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 来查找XIB中有没有视图view。如果有,则不会再走loadView。如果这个时候你的VC是没有xib的,哪么显然走这个方法后,是找不到任何view的,即self.view 仍为nil.然后,就跑loadview,这个时候会被触发,如果在loadView中,什么也不做,也不实例化一个View。哪么程序继续跑到viewDidLoad里,如果这里还是没有实例化VIEW。哪么这个VC就没有视窗。在这里很多时侯会出现一个误区 (死循环)。
好,下面来解释一下死循环的条件。
1、没有XIB。
2、ViewController中的loadView方法中没有做任何实例化self.view的操作。如:
-(void)loadView
{
写了一大堆代码,但最好并没有执行以下两种方式中的其中一种。
//方式一:实例化时使用[supper loadView];
//方式二 : self.view = [UIView alloc]....
}
3、在viewDidLoad中调用了self.view。
只要这三个条件同时满足,必定死循环。方式一时,调用了[Supper LoadView] 这个时候由父类产生了一个(0,20,Width,height )。这里的宽高根据是IPAD,还是IPHONE不同而不同,但原点坐标一定是(0,20)即去除状态条。方式二,没有对self.view作任可赋值,所以使得self.View = nil;
在条件二满足的情况下,程序运行到步骤三,这个时候,如果在这里调用了self.View。因为self.View在步骤二中为空,所以又回调到了loadView来,但因loadView中没有对self.View作实例化,于是在跑完loadView后,又继续跑viewDidLoad,但因ViewDidLoad中又没有实例化的情况下,使用了self.View.因此就出会现来回调用的现象。
好了,知疲知已方能百战百胜。解决死循环。
在步骤二中下手,处理方式有三:
a、把整个-(void)loadView 屏蔽掉。让父类自己来创建一个VIEW。这个是最常见的,因为ViewController产生的时候默认代码中是把这段代码给注释了的。
b、在loadView中添加一句[Supper LoadView];个人不太建议这样写吧,当然如果你理解了VIEW之间的关系,也无所谓。
c、在loadView中,使用已实例化的View对Self.View进行赋值。注:是使用=号赋值,而不是使用[self.view addSubView]因为此时self.view 是空指针,执行ADD操作会崩溃的。
另外,也可以在步骤三中下手,即使在步骤二中没有任何实例化VIEW操作,但在步骤三中进行了相应的实例化操作,仍可以解决的。这就是通常我们为什么不打开-loadView的注释,而直接在ViewDidLoad中进行添加视窗。
好了,上面的死循环介绍完了,顺便对使用下面两个方法来实例化视窗注意的地方。
1、[[UIScreen mainScreen] bounds] 返回的Rect是以(0,0)为坐标原点的大小,即包括了状态栏。
2、[[UIScreen mainScreen]applicationFrame] 返回的Rect是(0,20)为坐标点的大小,不包括状态栏。通常使用[supper loadView]所产生的的VIEW也就是这个所产生的。
2、[[UIScreen mainScreen]applicationFrame] 返回的Rect是(0,20)为坐标点的大小,不包括状态栏。通常使用[supper loadView]所产生的的VIEW也就是这个所产生的。
如果在loadView中,调用[supper loadview];或使用self.view =[[ [UIView alloc]initWithFrame:[[UIScreen mainScreen]applicationFrame] ]autorelease]; 这样在父子层之间使用AddSubView,你会看到一个偏移量为20的层叠视窗。原因就是这两个方式返回的RECT的原点都是(0,20);
DEMO:
A Viewcontroller
中的
- (void)loadView
{
[super loadView];
//self.view = [[[UIView alloc] initWithFrame:[[UIScreen mainScreen]applicationFrame]] autorelease];
//self.view = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 200, 400)];
self.view.backgroundColor = [UIColor greenColor];
BVC *bvc = [[BVC alloc]init];
NSLog(@"bvc View %@",bvc.view);
[self.view addSubview:bvc.view];
}
{
[super loadView];
//self.view = [[[UIView alloc] initWithFrame:[[UIScreen mainScreen]applicationFrame]] autorelease];
//self.view = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 200, 400)];
self.view.backgroundColor = [UIColor greenColor];
BVC *bvc = [[BVC alloc]init];
NSLog(@"bvc View %@",bvc.view);
[self.view addSubview:bvc.view];
}
B ViewController 中的
- (void)loadView
{
[super loadView];
//self.view = [[[UIView alloc] initWithFrame:[[UIScreen mainScreen]applicationFrame]] autorelease];
//self.view = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 200, 400)];
self.view.backgroundColor = [UIColor redColor];
CVC *cvc = [[CVC alloc]init];
[self.view addSubview:cvc.view];
}
{
[super loadView];
//self.view = [[[UIView alloc] initWithFrame:[[UIScreen mainScreen]applicationFrame]] autorelease];
//self.view = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 200, 400)];
self.view.backgroundColor = [UIColor redColor];
CVC *cvc = [[CVC alloc]init];
[self.view addSubview:cvc.view];
}
c ViewController中的
- (void)loadView
{
[super loadView];
//self.view = [[[UIView alloc] initWithFrame:[[UIScreen mainScreen]applicationFrame]] autorelease];
//self.view = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 100, 300)];
self.view.backgroundColor = [UIColor blueColor];
}
{
[super loadView];
//self.view = [[[UIView alloc] initWithFrame:[[UIScreen mainScreen]applicationFrame]] autorelease];
//self.view = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 100, 300)];
self.view.backgroundColor = [UIColor blueColor];
}
最后运行效果:
如果想子视图直接复盖父视图的大小可以使用[[UIScreen mainScreen] bounds]或者直接用UIVIEW实例化时指定原点。