UI控件初始化問(wèn)題:initWithFrame和initWithCoder、aweakFromNib的執(zhí)行
在iOS學(xué)習(xí)和程序開(kāi)發(fā)過(guò)程中,我們經(jīng)常會(huì)遇到一些自定義UI控件或控制器在初始化時(shí)出現(xiàn)問(wèn)題,尤其在大家剛開(kāi)始接觸時(shí),幾種初始化方法的作用以及調(diào)用的時(shí)機(jī)往往容易混淆,這也跟我們對(duì)iOS程序設(shè)計(jì)中,類(lèi)的創(chuàng)建和實(shí)例化的過(guò)程了解不透徹有關(guān)系。本文用一些小例子來(lái)簡(jiǎn)單梳理一下幾者的關(guān)系,后面再陸續(xù)討論一些復(fù)雜情況的深入對(duì)比。
問(wèn)題:一、什么時(shí)候用initWithFrame,什么時(shí)候用aweakFromNib、initWithCoder
?二、在初始化時(shí)控件自身的frame何時(shí)能獲得?layoutSubViews何時(shí)調(diào)用
首先,我們實(shí)例化一個(gè)(控件類(lèi)型)對(duì)象可以有多種方式:
(1)純代碼創(chuàng)建。創(chuàng)建自定義的UI控件類(lèi),然后實(shí)例化該類(lèi)型的對(duì)象。
(2)通過(guò)IB(Interface Builder)創(chuàng)建,就是俗稱(chēng)的“拖線”。當(dāng)我們創(chuàng)建好xib文件的時(shí)候,就相當(dāng)于創(chuàng)建好了控件類(lèi),但是如果不實(shí)例化,也是沒(méi)有用的,所以需要加載,這里用loadNibName來(lái)加載(實(shí)例化)UI控件。
1、搭建實(shí)驗(yàn)環(huán)境A,代碼創(chuàng)建控件(TestCodeingView繼承自UIView)

-(void)loadFromCoding
{
? ? TestCodeingView * viewCoding = [[TestCodeingView alloc]init];
? ? viewCoding.frame=CGRectMake(100,100,200,200);? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? viewCoding.backgroundColor=[UIColor greenColor];
? ? [self.view addSubview:viewCoding];
}
在TestCodeingView類(lèi)中對(duì)以下方法進(jìn)行重寫(xiě)-(instancetype)init
{
? ? self=[super init];
? ? NSLog(@" init =====> 執(zhí)行了");
? ? NSLog(@"此時(shí)view的frame====》 %@",NSStringFromCGRect(self.frame));
? ? return self;
}-(instancetype)initWithFrame:(CGRect)frame
{
? ? self=[super initWithFrame:frame];
? ? NSLog(@" initWithFrame =====> 執(zhí)行了");
? ? NSLog(@"此時(shí)view的frame====》 %@",NSStringFromCGRect(self.frame));
? ? return self;
}-(instancetype)initWithCoder:(NSCoder *)aDecoder
{
? ? self=[super initWithCoder:aDecoder];?
? ? NSLog(@" initWithCoder =====> 執(zhí)行了");
? ? return self;
}-(void)awakeFromNib
{
? ? NSLog(@" awakeFromNib =====> 執(zhí)行了");
}-(void)layoutSubviews
{
? NSLog(@" layoutSubviews =====> 執(zhí)行了");
? NSLog(@"此時(shí)view的frame====》 %@",NSStringFromCGRect(self.frame));
}
運(yùn)行結(jié)果:

然后更改部分代碼:

-(instancetype)initWithFrame:(CGRect)frame
{
? ? self=[super initWithFrame:frame];
? ? NSLog(@" initWithFrame =====> 執(zhí)行了");
? ? NSLog(@"此時(shí)view的frame====》 %@",NSStringFromCGRect(self.frame));
? ? UILabel * label = [[UILabel alloc]init];
? ? label.text=@"我是新建的label";
? ? label.backgroundColor=[UIColor orangeColor];
? ? self.label=label;
? ? [self addSubview:label];
? ? return self;?
}-(void)layoutSubviews
{
? ? NSLog(@" layoutSubviews =====> 執(zhí)行了");?
? ? NSLog(@"此時(shí)view的frame====》 %@",NSStringFromCGRect(self.frame));
? ? self.label.frame = CGRectMake((self.frame.size.width-150)/2,self.frame.size.height/2,150,30);
}

運(yùn)行結(jié)果:

小結(jié)一下:(1)純代碼創(chuàng)建的UI控件不執(zhí)行aweakFromNib方法和 initWithCoder方法。
(2)layoutSubciews方法在控件初始化完成后(自身和子控件的實(shí)例化結(jié)束)調(diào)用,方法中能獲得到當(dāng)前控件的frame,以便于給子控件布局。如有子控件,調(diào)用兩次。
?。?)系統(tǒng)在調(diào)用以上方法時(shí),有著特定的先后順序。
2、搭建實(shí)驗(yàn)環(huán)境B,Xib創(chuàng)建控件
通過(guò)xib加載自定義UI控件,如下圖,TestXibView類(lèi)為手動(dòng)創(chuàng)建的UI控件類(lèi),繼承自UIView

-(void)loadFromXib
{
? ? TestXibView * viewXib = [[[NSBundle mainBundle]loadNibNamed:@"testXibView" owner:nil options:nil] lastObject];?
? ? viewXib.center=self.view.center;
? ? [self.view addSubview:viewXib];
}


在TestCodeingView類(lèi)中對(duì)以下方法進(jìn)行重寫(xiě)

-(instancetype)init
{
? ? self=[super init];
? ? NSLog(@" init =====> 執(zhí)行了");? ?
? ? return self;
}-(instancetype)initWithFrame:(CGRect)frame
{
? ? self=[super initWithFrame:frame];?
? ? NSLog(@" initWithFrame =====> 執(zhí)行了");
? ? return self;
}-(instancetype)initWithCoder:(NSCoder *)aDecoder
{
? ? self=[super initWithCoder:aDecoder];?
? ? NSLog(@" initWithCoder =====> 執(zhí)行了");
? ? return self;
}-(void)awakeFromNib
{
? ? NSLog(@" awakeFromNib =====> 執(zhí)行了");?
}-(void)layoutSubviews
{
? ? NSLog(@" layoutSubviews =====> 執(zhí)行了");
}

運(yùn)行結(jié)果:

更改部分代碼,對(duì)Xib加載的控件使用代碼進(jìn)行修改 (添加了一個(gè)子控件和更改背景顏色):
-(instancetype)initWithCoder:(NSCoder *)aDecoder
{
? ? self=[super initWithCoder:aDecoder];
? ? NSLog(@" initWithCoder =====> 執(zhí)行了");
? ? UILabel * label = [[UILabel alloc]initWithFrame:CGRectMake(0,0,150,30)];
? ? label.text=@"我是新建的label";
? ? label.backgroundColor=[UIColor orangeColor];
? ? label.center=CGPointMake(self.center.x, self.frame.size.height-30);
? ? [self addSubview:label];
? ? return self;
}-(void)awakeFromNib
{
? NSLog(@" awakeFromNib =====> 執(zhí)行了");
? self.backgroundColor=[UIColor yellowColor];
}

運(yùn)行結(jié)果:

小結(jié)一下:(1)通過(guò)Xib創(chuàng)建UI控件,不會(huì)調(diào)用init和initwith方法。
?(2)創(chuàng)建一個(gè)控件類(lèi),和xib關(guān)聯(lián),是可以修改Xib中的屬性的。
?(3)一樣會(huì)調(diào)用layoutSubViews方法
(4)因?yàn)橥ㄟ^(guò)拖線和配置,已經(jīng)固定了控件的大小和布局,所以frame可以獲得
(5)initWithCoder和 aweakFromNib 在這里作用相同,都被系統(tǒng)調(diào)用
總結(jié)及延伸:
當(dāng)我們弄清楚控制器加載的各種情況后,相對(duì)于用代碼,使用IB和xib文件來(lái)組織UI,可以省下大量代碼和時(shí)間,從而得到更快的開(kāi)發(fā)速度;同時(shí),Xib最大的問(wèn)題在于其設(shè)置往往并非最終設(shè)置,在代碼中你將有機(jī)會(huì)覆蓋你在xib文件中進(jìn)行的UI設(shè)計(jì),造成錯(cuò)誤和混亂。
說(shuō)了好多,總結(jié)一下也無(wú)非幾句話:
1、用Xib創(chuàng)建控件,對(duì)于控件的后續(xù)操作都寫(xiě)在initWithCoder或aweakFromNib方法中;
2、純代碼寫(xiě)創(chuàng)建的控件,對(duì)于控件的后續(xù)操作都寫(xiě)在initWithFrame方法中;
3、添加子控件時(shí),注意布局(frame的獲得),合理靈活的使用xib加載控件;
4、至于initWithCoder和aweakFromNib的區(qū)別在后面再做討論(關(guān)于通過(guò)xib加載控制器)。