问题描述
对于经常使用xib的童鞋们来说,使用xib来初始化一些简单的viewController,简直是犹如探囊取物。但是,今天我画好xib之后,使用老套路,[[ViewController alloc] init]赋值给视图控制器,然后push之后发现,我在xib上添加的东西都没有,这就是遇到的这个问题。
问题形成原因
经过一番的查找,终于找到了出现问题的原因,原因是我使用的viewController的父类不恰当的重写了loadView方法。
上述问题出现时使用- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil;方法可以正常加载自定义的内容。那么我们就不得不看一下这个神秘的方法。
/*
The designated initializer. If you subclass UIViewController, you must call the super implementation of this
method, even if you aren't using a NIB. (As a convenience, the default init method will do this for you,
and specify nil for both of this methods arguments.) In the specified NIB, the File's Owner proxy should
have its class set to your view controller subclass, with the view outlet connected to the main view. If you
invoke this method with a nil nib name, then this class' -loadView method will attempt to load a NIB whose
name is the same as your view controller's class. If no such NIB in fact exists then you must either call
-setView: before -view is invoked, or override the -loadView method to set up your views programatically.
*/
-- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil NS_DESIGNATED_INITIALIZER;
可以看到,苹果官方的注释中说到,不管你有没有使用NIB,这个方法都会被调用,此时会将两个参数传为nil;并说到,如果nib传入的参数为nil,那么loadView方法就会尝试加载一个NIB,这个NIB的名字与该viewController的类名相同;如果没有与之相对应的NIB,那么必须在用view之前设置view或者重写-loadView方法来自定义view。
可能看了这个还是云里雾里,我们来看一下李明杰老师的博客中对loadView方法的介绍:
loadView
1.什么时候被调用?
每次访问UIViewController的view(比如controller.view、self.view)而且view为nil,loadView方法就会被调用。
2.有什么作用?
loadView方法是用来负责创建UIViewController的view
3.默认实现是怎样的?
默认实现即[super loadView]里面做了什么事情。
1> 它会先去查找与UIViewController相关联的xib文件,通过加载xib文件来创建UIViewController的view
如果在初始化UIViewController指定了xib文件名,就会根据传入的xib文件名加载对应的xib文件
[[MJViewController alloc] initWithNibName:@"MJViewController" bundle:nil];
如果没有明显地传xib文件名,就会加载跟UIViewController同名的xib文件
[[MJViewController alloc] init]; // 加载MJViewController.xib
2> 如果没有找到相关联的xib文件,就会创建一个空白的UIView,然后赋值给UIViewController的view属性,大致如下
self.view = [[[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame] autorelease];
// applicationFrame的值是:{{x = 0, y = 20}, {width = 320, height = 460}}
[super loadView]里面就大致完成1>和2>中叙述的内容
4.怎样正确使用这个方法?
大家都知道UIViewController的view可以通过xib文件来创建,但是在某些情况下,xib不是那么地灵活,所以有时候我们想通过代码来创建UIView,比如:
self.view = [[[UIWebView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame] autorelease];
如果想通过代码来创建UIViewController的view,就要重写loadView方法,并且不需要调用[super loadView],因为在第3点里面已经提到:若没有xib文件,[super loadView]默认会创建一个空白的UIView。我们既然要通过代码来自定义UIView,那么就没必要事先创建一个空白的UIView,以节省不必要的开销。
正确的做法应该是这样:
- (void)loadView {
self.view = [[[UIWebView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame] autorelease];
}
不需要调用[super loadView],你调用了也不会出错,只是造成了一些不必要的开销。
总结一句话,苹果设计这个方法就是给我们自定义UIViewController的view用的
正确使用loadView的姿势
尽量不要在基础的自定义类中使用loadView,如果要使用,就一定要保证xib也能正常加载的逻辑。
网友评论