關於從xib加載視圖

作者:Love@YR
鏈接:http://blog.csdn.net/jingqiu880905/article/details/51745617
請尊重原創,謝謝

經常會看到initWithNibName,initWithCoder,awakeFromNib,loadNibNamed 這些方法,各種頭大,網上資料不少,能說清楚的不多,於是今天寫了個demo來測試下這些方法何時調用。

首先,我們從視覺上區分一下loadNibName和initWithNibName。
initWithNibName 是由UIViewController及其子類調用
如:

     AViewController *avc = [[AViewController alloc]initWithNibName:@"AViewController" bundle:nil];

而loadNibName則是這樣:

 NSArray *objectsInNib = [[NSBundle mainBundle] loadNibNamed:@"BView" owner:nil options:nil];
 for(id item in objectsInNib){
if([item isKindOfClass:[yourview class]]){
//找到了你需要的對象,do what you want
}

在這個時候生成後會讓引用計數器變爲1,但是需要注意得owner:self ,在owner自己dealloc得時候,系統會自動幫你將你得youview得引用計數器-1。

在xib裏,第一種(一般我們創建 viewcontroller時勾選了nib生成的)
通常是這樣的:
這裏寫圖片描述

注意這裏的file’s owner是你的view controller類

這裏寫圖片描述

底下這個view就是這個view controller的根view

第二種通常是這樣的:

這裏寫圖片描述

注意看,這裏的file’s owner是NSObject

這裏寫圖片描述

這裏我拖了3個控件,其中view這個所屬的類名爲BView,同樣其它控件我也需要給它創建類並連接。

這裏寫圖片描述

默認uiview佔滿屏,改下其size爲freeform即可改變其大小。

一般來說我們調用loadNibNamed的到的數組即這個xib上的所有控件,我們是想要拿一些控件如view,cell等而不是viewcontroller。(這也是最常用的用途)
但是,這裏我們同樣可以拖入viewcontroller, navigationcontroller等任意對象。
如圖:

這裏寫圖片描述

那麼這裏我們得到的數組裏就能有viewcontroller這個對象了。
那麼同樣兩種創建viewcontroller的方法,分別調用了什麼呢?
測試結果顯示用initWithNibName方法,會調用此CustomViewController的initWithNibName,viewDidLoad

而用loadNibNamed時,執行到此句時,就會調用此數組裏所有對象的initWithCoder,awakeFromNib方法。
比如我在一個xib里加入了一個view,兩個view controller。執行的log顯示當調用loadNibNamed時打印了:
BView——-initWithCoder———-called
AVC——-initWithCoder———-called
bVC——-initWithCoder———-called
BView——-awakeFromNib———-called
AVC——-awakeFromNib———-called
bVC——-awakeFromNib———-called
然後我拿出aVC對象,present,並沒有執行AVC的viewDidLoad。
這個問題 http://www.cnblogs.com/iOS-mt/p/4362335.html 這篇文章也說了,後續再研究。
這下清晰多了。
然後 http://blog.csdn.net/richard_rufeng/article/details/26508977 這篇文章說initWithNibName方法是延遲加載 loadNibNamed方法是即時加載,具體原理先不管了。就這樣。

——————————我是分割線———————————————-

上面說了loadNibNamed方法一般用來加載控件對象的。下面我們來看用此方法生成一個CustomView對象和直接alloc一個此對象調用的方法有何區別:

在某個controller裏我創建了avc並present

AViewController *avc = [[AViewController alloc] init];
  [self presentViewController:avc animated:YES completion:nil];

在AViewController的viewDidLoad裏:

    NSArray *bvarr = [[NSBundle mainBundle] loadNibNamed:@"BView"  owner:nil options:nil];
    BView *bv = [bvarr objectAtIndex:0];
//    [bv setFrame: CGRectMake(10, 10, 100, 100)];//此句話無效
    [self.view addSubview:bv];

這裏BView.xib裏只有一個UIView的子類BView。和上面AVC一樣,loadNibNamed時會依次調用BView的initWithCoder,awakeFromNib。還有view controller所沒有的layoutSubviews。

然後測試發現
1. 對於bview的子控件,都得在awakeFromNib或者layoutSubviews裏設置其屬性才能生效,在initWithCoder是不行的。這篇文章也說了。
2. self.frame必須在layoutSubviews裏設置才能生效!!
3. initWithCoder,awakeFromNib裏設置顯示的效果都不準確。網上有人說是因爲還沒加載完。且如上段代碼所註釋,直接[bv setFrame: CGRectMake(10, 10, 100, 100)]也沒有用。

代碼:

BView.m

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {
        NSLog(@"BView-------initWithCoder----------called");
    }
    return self;
}

-(void)awakeFromNib
{
     NSLog(@"BView-------awakeFromNib----------called");

    self.bviewLabel.text = @"b商品";
    self.bviewLabel.backgroundColor = [UIColor purpleColor];
    self.bviewLabel.frame = CGRectMake(5, 10, 50, 30);//當不用autolayout的時候代碼設置self.bviewLabel.frame是生效的

    self.backgroundColor = [UIColor greenColor];
//    self.frame = CGRectMake(10, 10, 300, 200);
}

-(void)layoutSubviews
{
    NSLog(@"BView-------layoutSubviews----------called");
     self.frame = CGRectMake(10, 10, 300, 200);//此句必須寫這個方法裏
}

——————————我是分割線———————————————-

下面看看直接alloc生成此view對象會調用哪些方法:

當不在外設置view的frame時,也不在initWithFrame,init裏設置frame時:

    CView *cv = [[CView alloc]init];
    [self.view addSubview:cv];

打印顯示先後走了initWithFrame,init方法

當在initWithFrame或者init裏或者外部cv setFrame時,會觸發layoutSubviews。
打印顯示先後走了initWithFrame,init,layoutSubviews方法

init裏的setFrame會覆蓋initWithFrame裏的,layoutSubviews裏如果寫了setFrame則會覆蓋init裏的。

-(instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        NSLog(@"CView-------initWithFrame----------called");

     self.frame = CGRectMake(0, 0, 40, 40);
     self.backgroundColor = [UIColor greenColor];
    UILabel * clableview = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 10, 10)];
    clableview.text = @"c商品";
    clableview.backgroundColor = [UIColor purpleColor];
    [self addSubview:clableview];
      }
    return self;
}

- (instancetype)init
{
    if (self = [super init]) {
        NSLog(@"CView-------init----------called");
     // self.frame = CGRectMake(10, 10, 20, 80);
    }
    return self;
}

-(void)layoutSubviews
{
    NSLog(@"CView-------layoutSubviews----------called");
    // self.frame = CGRectMake(10, 10, 80, 20);
}

當使用 CView *cv = [[CView alloc]initWithFrame:CGRectMake(10, 10, 10, 10)];
會調用initWithFrame和layoutSubviews不會調用init方法。

所以可以直接在initWithFrame裏面去設置frame backgroundcolor subview等。

發佈了56 篇原創文章 · 獲贊 28 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章