UI控件初始化問題:initWithFrame和initWithCoder、aweakFromNib的執行


在iOS學習和程序開發過程中,我們經常會遇到一些自定義UI控件或控制器在初始化時出現問題,尤其在大家剛開始接觸時,幾種初始化方法的作用以及調用的時機往往容易混淆,這也跟我們對iOS程序設計中,類的創建和實例化的過程了解不透徹有關系。本文用一些小例子來簡單梳理一下幾者的關系,后面再陸續討論一些復雜情況的深入對比。

問題: 一、什么時候用initWithFrame,什么時候用aweakFromNib、initWithCoder

        二、在初始化時控件自身的frame何時能獲得?layoutSubViews何時調用

 

首先,我們實例化一個(控件類型)對象可以有多種方式:

(1)純代碼創建。創建自定義的UI控件類,然后實例化該類型的對象。

(2)通過IB(Interface Builder)創建,就是俗稱的“拖線”。當我們創建好xib文件的時候,就相當於創建好了控件類,但是如果不實例化,也是沒有用的,所以需要加載,這里用loadNibName來加載(實例化)UI控件。

1、搭建實驗環境A,代碼創建控件(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類中對以下方法進行重寫

-(instancetype)init
{
    self=[super init];
    NSLog(@" init =====> 執行了");
    NSLog(@"此時view的frame====》 %@",NSStringFromCGRect(self.frame));
    return self;
    
}

-(instancetype)initWithFrame:(CGRect)frame
{
    self=[super initWithFrame:frame];
    NSLog(@" initWithFrame =====> 執行了");
    NSLog(@"此時view的frame====》 %@",NSStringFromCGRect(self.frame));
    return self; 
}

-(instancetype)initWithCoder:(NSCoder *)aDecoder
{
    self=[super initWithCoder:aDecoder];  
    NSLog(@" initWithCoder =====> 執行了");
    return self;
}
-(void)awakeFromNib
{
    NSLog(@" awakeFromNib =====> 執行了");
}

-(void)layoutSubviews
{
   NSLog(@" layoutSubviews =====> 執行了");
   NSLog(@"此時view的frame====》 %@",NSStringFromCGRect(self.frame));
 
}

 

運行結果:

 

然后更改部分代碼:

-(instancetype)initWithFrame:(CGRect)frame
{
    self=[super initWithFrame:frame];
    NSLog(@" initWithFrame =====> 執行了");
    NSLog(@"此時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 =====> 執行了");   
    NSLog(@"此時view的frame====》 %@",NSStringFromCGRect(self.frame));
    self.label.frame = CGRectMake((self.frame.size.width-150)/2,self.frame.size.height/2, 150, 30);
    
}

運行結果:

小結一下:(1)純代碼創建的UI控件不執行aweakFromNib方法和 initWithCoder方法。 

     (2)layoutSubciews方法在控件初始化完成后(自身和子控件的實例化結束)調用,方法中能獲得到當前控件的frame,以便於給子控件布局。如有子控件,調用兩次。

     (3)系統在調用以上方法時,有着特定的先后順序。

 

 

2、搭建實驗環境B,Xib創建控件

 

通過xib加載自定義UI控件,如下圖,TestXibView類為手動創建的UI控件類,繼承自UIView

 

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

 

 

在TestCodeingView類中對以下方法進行重寫

 

-(instancetype)init
{
    self=[super init];
    NSLog(@" init =====> 執行了");    
    return self;

}


-(instancetype)initWithFrame:(CGRect)frame
{
    self=[super initWithFrame:frame];  
    NSLog(@" initWithFrame =====> 執行了");
    
    return self;
   
}



-(instancetype)initWithCoder:(NSCoder *)aDecoder
{
    self=[super initWithCoder:aDecoder];   
    NSLog(@" initWithCoder =====> 執行了"); 
    return self;

}


-(void)awakeFromNib
{
    NSLog(@" awakeFromNib =====> 執行了");  
}


-(void)layoutSubviews
{
    NSLog(@" layoutSubviews =====> 執行了");
}

運行結果:

 

 

更改部分代碼,對Xib加載的控件使用代碼進行修改 (添加了一個子控件和更改背景顏色):

 

-(instancetype)initWithCoder:(NSCoder *)aDecoder
{
    self=[super initWithCoder:aDecoder];
    
    NSLog(@" initWithCoder =====> 執行了");
    
    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 =====> 執行了"); 
   self.backgroundColor=[UIColor yellowColor];
    
}

 

運行結果:

 

 

小結一下:(1)通過Xib創建UI控件,不會調用init和initwith方法。

              (2創建一個控件類,和xib關聯,是可以修改Xib中的屬性的。

              (3一樣會調用layoutSubViews方法

     (4因為通過拖線和配置,已經固定了控件的大小和布局,所以frame可以獲得

              (5initWithCoder和 aweakFromNib 在這里作用相同,都被系統調用

  

總結及延伸:

當我們弄清楚控制器加載的各種情況后,相對於代碼,使用IB和xib文件來組織UI,可以省下大量代碼和時間,從而得到更快的開發速度;同時,Xib最大的問題在於其設置往往並非最終設置,在代碼中你將有機會覆蓋你在xib文件中進行的UI設計,造成錯誤和混亂。

 

說了好多,總結一下也無非幾句話:

1、用Xib創建控件,對於控件的后續操作都寫在initWithCoder或aweakFromNib方法中;

2、純代碼寫創建的控件,對於控件的后續操作都寫在initWithFrame方法中;

3、添加子控件時,注意布局(frame的獲得),合理靈活的使用xib加載控件;

4、至於initWithCoder和aweakFromNib的區別在后面再做討論(關於通過xib加載控制器)。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM