xib 原理倍阐、嵌套概疆、可視化、繼承

xib嵌套

封裝了一個view收捣,它自帶有xib届案。想讓它能在其他xib、SB中使用

1.綁定xib的File Owner
(此xib的所有者罢艾,管理這個xib的邏輯楣颠、交互等,類似于ViewControllerview的管理)

綁定File Owner.png

(這里你可能會疑問為什么不直接綁定view為此類咐蚯,如下圖童漩。這種做法也可以,但只適用于用代碼創(chuàng)建的場景春锋,如自定義帶xib的UITableViewCell矫膨,每個cell是由代碼創(chuàng)建的。不能內(nèi)嵌到其他xib期奔、SB中使用侧馅,后面有詳細解釋)

綁定view.png

2.在view的.m文件中實現(xiàn)下面代碼就行了

- (instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super initWithCoder:coder];
    if (self) {
        [self loadViewFromXib];
    }
    return self;
}

- (void)loadViewFromXib
{
    // 取xib中view的方法一:
    UIView *contentView = [[NSBundle bundleForClass:[self class]] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil].firstObject;
   
    // 取xib中view的方法二:
    // UINib *nib = [UINib nibWithNibName:NSStringFromClass([self class]) bundle:[NSBundle bundleForClass:[self class]]];
    // UIView *contentView = [nib instantiateWithOwner:self options:nil].firstObject;

    /*
      這里取view有兩種方法,方法一一步完成,方法二拆成了兩步呐萌。
      方法二的好處在于我們可以保留取出來的nib馁痴,以后直接用nib生成view,不會像方法一總是會重復去取nib肺孤。
      方法二常見于帶xib的TableViewCell罗晕,因為cell會大量重用济欢,所以方法二取一次nib保存后,以后要用cell只用執(zhí)行它的第二句代碼來生成view小渊,更加高效
      一般情況下法褥,兩個沒什么區(qū)別,用哪個看個人喜好
    */

    contentView.frame = self.bounds;
    contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth| UIViewAutoresizingFlexibleHeight;
    [self addSubview:contentView];
}


xib可視化

可以讓封裝好的xib酬屉,在其他xib或者SB中實時預覽

只需在上面代碼基礎上加入以下三步:

  1. view的.h中聲明 IB_DESIGNABLE半等。(聲明此視圖需要支持預覽)
  2. 要寫 initWithFrame:方法。(xcode預覽的工具梆惯,顯示view時會創(chuàng)建這個View酱鸭,此時走的view的這個初始化方法)
  3. [NSBundle bundleForClass:[self class]]]取對對應的Bundle。(可能實時運行的預覽工具垛吗,取資源文件的位置和app實際運行時不一樣)


完整代碼如下:(以后可直接復制使用)

.h

IB_DESIGNABLE
@interface CustomView : UIView

@end
.m

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self loadViewFromXib];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super initWithCoder:coder];
    if (self) {
        [self loadViewFromXib];
    }
    return self;
}

- (void)loadViewFromXib
{
    // 對于Bundle不太了解凹髓,但這里要用[NSBundle bundleForClass:[self class]]]取對對應的Bundle,而不是用mainBundle或者nil
    UIView *contentView = [[NSBundle bundleForClass:[self class]] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil].firstObject;
    contentView.frame = self.bounds;
    contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth| UIViewAutoresizingFlexibleHeight;
    [self addSubview:contentView];
}

// 順便一提屬性使用IBInspectable可在xib的右側的屬性(Attributes inspector)標簽欄中看到
@property (nonatomic, assign) IBInspectable BOOL semicircle;///< 始終一般高度的圓角

// 使用IBOutlet可在連接(Connections inspector)標簽欄中看到怯屉,一般用來連線到File Owner
@property (nonatomic, weak) IBOutlet id customDelegate;

如果一直沒顯示出來:
1.類名不能綁定在view上蔚舀,要綁在File Owner上,否則會死循環(huán)crash
2.使用下面的【view Debugging】方法

view Debugging

當為view增加 IB_DESIGNABLE時锨络,可能經(jīng)常出現(xiàn) error: IB Designables: Failed to render and update auto layout status for UIView (i5M-Pr-FkT): The agent crashed的情況

此時有兩種調(diào)試方法:
1.在xib或者sb中赌躺,選擇自定義視圖,然后選擇 Editor- Debug Selected Views羡儿。這會重新對這view運行 IBDesignableAgentCocoaTouch礼患,可以進入視圖的斷點進行調(diào)試

2.在 ~/Library/Logs/DiagnosticReports目錄中有命名為 IBDesignablesAgentCocoaTouch_*.crash的崩潰日志,記錄了堆棧信息

預覽專用方法

預覽專用方法掠归,程序實際運行時不調(diào)用缅叠。
用于在其他xib、SB中預覽你customView時虏冻,配置一些參數(shù)的默認值肤粱,以便能完整的預覽到customView效果

/// 預覽時調(diào)用,程序實際運行時不調(diào)用
- (void)prepareForInterfaceBuilder
{
    self.arrTitle = @[@"預覽", @"預覽文案", @"在程序運行時不會執(zhí)行厨相,預覽文案預覽文案", @"預覽文案领曼,在程", @"預覽文案,在程序運行時不會執(zhí)行預覽文案蛮穿,在程序運行時不會執(zhí)行"];
}


xib原理

為什么不先講原理呢庶骄?怕嚇跑你們??

xib本身是xml格式的資源文件,上面記錄了什么控件應該擺在哪里践磅,控件本身設置了哪些屬性等瓢姻,和你的圖片、音視頻差不多音诈,所以本身是無法"直接"繼承的幻碱。
是的,我們可以"間接"達到繼承的效果细溅,后面會說褥傍。

名詞釋義:
xib:xml格式的文件,記錄控件喇聊、屬性及其之間位置關系恍风。(我們在xcode中直接看到的)
nib:xib被加密、序列化后的二進制文件(data)誓篱。(app打包后朋贬,將包拆開后可以看到.nib后綴名的文件)  

序列化:歸檔(將xib加密成nib的過程)
反序列化:解檔(將nib解密,提取到內(nèi)存中窜骄,成為我們能直接使用的類的過程)
頂級對象:xib面板左側對象邊欄中的最外層的對象锦募,如最外層的view。instantiateWithOwner:和loadNibNamed:owner:方法返回的數(shù)組中邻遏,保存的都是頂級對象(原諒我表述不清糠亩,自己的理解不深)
xib加載流程:
  1. 提取nib文件到內(nèi)存中
    Bundle中取出nib文件,為二進制文件准验,加入到內(nèi)存中

  2. 對原xib中所有view對象進行解檔
    a) 從內(nèi)存中的二進制數(shù)據(jù)赎线,取出原xib中的各view對應那部分data
    b) 通過調(diào)用initWithCoder:初始化方法,創(chuàng)建原xib中的所有view糊饱,將上面的那部分data作為入?yún)魅?br> c) 這里是每個view進行反序列化垂寥,將二進制文件轉為實際的類。實際上不需要我們親自來反序列化另锋,在initWithCoder:方法中調(diào)用[super initWithCoder:coder]即可滞项,系統(tǒng)的根類中已經(jīng)默認做好了
    d) 注意??:每個view(包括頂級對象view)在xib中綁定的什么類,就會創(chuàng)建這個類砰蠢。例如一個View沒有綁定類名蓖扑,默認系統(tǒng)的UIView類,那么實際就是調(diào)用的[UIView initWithCoder:aData]台舱,這個view解檔完成后就是UIView的實例律杠;如果一個View綁定類名為CustomView,那么實際就是調(diào)用的[CustomView initWithCoder:aData]竞惋,然后就進入到CustomView類中的initWithCoder:方法了柜去,這個view解檔完成后就是CustomView的實例。
    e) 注意??:在initWithCoder:方法中拆宛,不能使用xib嗓奢、SB連線出來的屬性,此時連線的屬性都為nil浑厚,因為現(xiàn)在還沒開始關聯(lián)屬性

  3. 關聯(lián)屬性和方法
    對連線到自己類股耽、File Owner根盒、Object中的屬性進行弱引用關聯(lián),并關聯(lián)事件物蝙。然后這三個地方就可以使用連線過來的屬性和響應連線的過來方法了炎滞。(下面會講怎么連線到這三個地方)

  4. 解檔完成
    原xib中各個view解檔完成后,調(diào)用各自的awakeFromNib方法诬乞,告訴你xib已經(jīng)完全ok册赛,可以直接使用了。現(xiàn)在你可以在awakeFromNib方法中震嫉,使用xib森瘪、SB連線出來的屬性了。

拓展點:xib票堵、SB可以連線到哪些地方

1)連線到自己類
自己的東西(子視圖扼睬、手勢、Object類等)换衬,能通過連線痰驱,被自己使用,這沒毛病瞳浦。

連線到自己類.png

2)連線到File Owner
自己的所有者使用自己東西担映,也挺合理。
File Owner一般作為管理xib的地方叫潦,像ViewController蝇完、包含此xib的視圖

連線到File Owner.png

3)連線到Object
Object是任意類,一般是繼承于NSObject的邏輯類矗蕊,用于處理視圖中的邏輯部分短蜕,這就很厲害了。
Object其實分為ObjectExternal Object兩種傻咖,前者xib朋魔、SB會直接在解檔的時候創(chuàng)建一個,后者則是需要在xib初始化時卿操,傳入的已存在的實例警检,不由xib創(chuàng)建。

使用Object很簡單害淤,直接拖入一個Object扇雕,然后綁定類名,就可以關聯(lián)屬性及事件了
這里的CustonViewManager類窥摄,可以當做專門處理CustonView中邏輯的類

使用Object.png

使用External Object稍微復雜點镶奉,拖入一個External Object后,需要先綁定類名(class)及標識符(Identifier)
這里的TestViewController是CustonView的File Owner,TestViewControllerManager類可以當做是處理TestViewController中邏輯的類

綁定類名及標識符.png

然后在xib初始化時哨苛,傳入External Object的實例鸽凶。
傳入External Object的實例.png

最后就可以在TestViewControllerManager中也使用CustonView中的東西啦
External Object中關聯(lián)屬性及事件.png

所以,例如xib移国、SB中有一個button時吱瘩,我們?nèi)绻麑⑦@個button連線到這三個地方,那么在xib解檔后迹缀,這三個地方都能修改這個button的屬性。
同理蜜徽,如果將這個button的點擊事件連線到到這三個地方祝懂,那么這三個地方都能響應button的點擊事件(三個地方點擊事件的執(zhí)行順序暫時還沒測試過,不過我覺得這里的作用是分開執(zhí)行不相關聯(lián)的邏輯拘鞋,不能太依靠事件的執(zhí)行順序)砚蓬。
這樣就能給代碼瘦身,封裝出更優(yōu)雅的View盆色。不過也因為太靈活灰蛙,所以使用時需要謹防矯枉過正

拓展點:將類名綁File Owner和綁頂級對象view的區(qū)別

這點文章開頭xib加載流程中都有提到過。

CustonView.xib為例:
你如果將頂級對象view的類名綁定為CustonView隔躲,那么在xib解檔過程中摩梧,對頂級對象view解檔后,返回的就是CustonView這個實例宣旱。也就是loadNibNamed: owner:self options:這個方法返回的數(shù)組中仅父,你拿到的就是custonView的實例

你如果將File Owner的類名綁定為CustonView,那么CustonView類CustonView.xib的管理者浑吟,CustonView.xib只負責給CustonView類提供打包好的view笙纤。也就是loadNibNamed: owner:self options:這個方法返回的數(shù)組中,你拿到的就是UIView的實例

多數(shù)情況下组力,我們都是綁定File Owner的類名省容,為什么呢?舉個栗子??

我封裝了一個RedButton的控件燎字,它自帶有RedButton.xib腥椒,里面是一些圖片、文案等轩触,且在RedButton.xib中的綁定頂級對象view為RedButton寞酿。這時我在mainViewController.xib中需要用到封裝好的RedButton,我就拖一個button到xib脱柱,然后將類名改為RedButton伐弹。

接下來我們在腦海中模擬一下mainViewController.xib解檔的整個過程:

  1. mainViewController.xib開始解檔,它要通過二進制數(shù)據(jù)(nib)創(chuàng)建原xib中所有的view,所以調(diào)用每個view的initWithCoder:方法
  2. 接著RedButton被調(diào)用initWithCoder:方法惨好,此時initWithCoder:時沒有寫任何代碼的煌茴,程序繼續(xù)走,RedButton實例生成成功日川。

但是最后我們發(fā)現(xiàn)蔓腐,mainViewController.xib中RedButton這部分是一片空白。這時我們回想一下就會發(fā)現(xiàn)龄句,mainViewController.xib只是會通過initWithCoder:方法創(chuàng)建了RedButton這個類回论,單純的創(chuàng)建這個類,并不會加載RedButton.xib這個文件分歇,所以RedButton缺失了視圖元素傀蓉。

那么我們將代碼改下,在initWithCoder:中加載RedButton.xib职抡,將其頂級對象view作為自己子視圖:

- (instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super initWithCoder:coder];
    if (self) {
        [self loadViewFromXib];
    }
    return self;
}

- (void)loadViewFromXib
{
    UINib *nib = [UINib nibWithNibName:NSStringFromClass([self class]) bundle:[NSBundle bundleForClass:[self class]]];
    UIView *contentView = [nib instantiateWithOwner:self options:nil].firstObject;
    contentView.frame = self.bounds;
    [self addSubview:contentView];
}

重新運行程序后葬燎,會發(fā)現(xiàn)程序crash了,堆棧信息中顯示陷入了死循環(huán)缚甩。我們重新理一下現(xiàn)在的調(diào)用順序:

  1. mainViewController.xib開始解檔谱净,它要通過二進制數(shù)據(jù)(nib)創(chuàng)建原xib中所有的view,所以調(diào)用每個view的initWithCoder:方法
  2. 接著RedButton要開始解檔擅威,通過調(diào)用[RedButton initWithCoder:aData]壕探,此時initWithCoder:中要加載RedButton.xib
  3. RedButton.xib開始解檔,調(diào)用每個view的initWithCoder:方法裕寨,包括頂級對象view浩蓉。
  4. 因為頂級對象view的類名綁定的是RedButton,頂級對象view開始解檔宾袜,調(diào)用[RedButton initWithCoder:aData]捻艳,果然進入了死循環(huán)

這要怎么解決呢?將xib中綁頂級對象view的類名改為綁File Owner的類名就行了庆猫。
這說明封裝的帶xib的view认轨,如果要支持嵌套在其他xib中,則不能綁定頂級對象view的類名月培,只能通過綁定File Owner的類名嘁字,將RedButton.xib作為單純的視圖元素加到自己的view上。
這也就是為什么綁File Owner的類名的做法更常見杉畜。

那不能將封裝的帶xib的view纪蜒,綁頂級對象view的類名么?可以的此叠,上面代碼不變纯续,xib綁頂級對象view的類名不變。不過只能用于代碼創(chuàng)建xib的場景,如VC中猬错,用代碼創(chuàng)建自定義view(RedButon *btn = [self loadNibNamed:@"RedButton" owner:self options:].firstObject),又例如在UITableView的代理中窗看,創(chuàng)建帶xib的Cell。

結論:在xib中
綁頂級對象view的類名:只能用于代碼創(chuàng)建此類
綁File Owner的類名:代碼創(chuàng)建此類倦炒、嵌套到其他xib中都可以(常用)

xib繼承

以聊天頁面為例(如QQ)显沈,里面的每條消息都是一行cell,大致有這幾種cell:文字消息行逢唤、圖片消息行拉讯、視頻消息行、紅包消息行鳖藕。這些cell都有共同的部分遂唧,就是左右的人物頭像,以及氣泡背景吊奢。
一般第一反應就是做成繼承的類型,父類cell中有共同部分纹烹,子類cell中只有各自差異化的東西页滚,如父類cell有頭像、氣泡铺呵,文字消息子cell中只有文字裹驰,圖片消息子cell中只有圖片。

具體怎么做呢片挂?別急幻林,還需要了解兩個東西:
1.子類xib中的元素,可以連線到父類中音念。不管是綁頂級對象view還是綁File Owner沪饺。
這里舉例不當,沒必要同時連線到父類和子類中闷愤,因為子類會繼承父類中的屬性整葡,希望別被我誤導了

子類xib連線到父類.png

2.我一般使用UITableView時會開始就注冊cell讥脐,這樣就不用在- tableView: cellForRowAtIndexPath:中判斷cell是不是存在遭居,因為注冊了cell后,然后從重用隊列中取cell時旬渠,如果取不到系統(tǒng)會自動創(chuàng)建一個新的cell返回給你俱萍。
下面是重點:

注冊cell有兩種方式:
1.使用[_tableView registerNib: forCellReuseIdentifier:]方法: 通過nib創(chuàng)建cell
用于創(chuàng)建自帶xib的cell,也就是cell.xib中告丢,必須綁定頂級對象的類名為當前cell枪蘑。
創(chuàng)建時,會走cell的initWithCoder:方法,不會走別的初始化方法

2.使用[_tableView registerClass: forCellReuseIdentifier:]方法: 通過類名來創(chuàng)建cell
一般用于創(chuàng)建沒有xib的cell腥寇。如果cell有xib成翩,也要使用這種注冊方法怎么辦,在cell.xib中赦役,綁定File Owner為當前cell麻敌。
創(chuàng)建時,會走cell的initWithStyle: reuseIdentifier:方法掂摔,不會走別的初始化方法

現(xiàn)在我們就可以試著想怎么實現(xiàn)上面說的聊天信息cell的繼承:
前提:代碼上术羔,子類cell都繼承父類cell,如文字消息cell乙漓、圖片消息cell级历、視頻消息cll等,都繼承自父類消息cell
那么叭披,父類cell是否擁有xib寥殖、父類或子類cell的xib中的內(nèi)容是完整的還是一部分、這些xib是通過registerNib的方式注冊還是通過registerClass的方式注冊涩蜘,這些組合起來就有很多可能嚼贡。

方案一:

Demo: 方案一Demo

父類和子類都有各自的xib,父類的xib是公共部分同诫,子類的xib是差異部分粤策。(xib全部綁定的是File Owner)
子類通過registerClass注冊, 在初始化時误窖,先加載父類xib中內(nèi)容叮盘,addSubView到cell上。然后加載自己xib霹俺,addSubView到指定區(qū)域(氣泡)中柔吼。


方案一xib.png

先注冊cell (一定要使用registerClass的方式注冊,不能是registerNib

// 首先使用registerClass的方式注冊子類cell
[_tableView registerClass:[TextChatCell class] forCellReuseIdentifier:NSStringFromClass([TextChatCell class])];
    [_tableView registerClass:[ImageChatCell class] forCellReuseIdentifier:NSStringFromClass([ImageChatCell class])];

// 然后就可以用重用獲取cell了
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSDictionary *dict = _arrData[indexPath.row];
    
    NSString *type = dict[@"type"];
    if ([type isEqualToString:@"text"]) {
        // text
        TextChatCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([TextChatCell class]) forIndexPath:indexPath];
        return cell;
    } else {
        // image
        ImageChatCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([ImageChatCell class]) forIndexPath:indexPath];
        return cell;
    }
}

父類cell實現(xiàn)

@implementation BaseChatCell
#pragma mark - Life Circle
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        [self loadViewFromXib];
    }
    return self;
}

/// 加載父類xib
- (void)loadViewFromXib
{
    UINib *nib = [UINib nibWithNibName:NSStringFromClass([BaseChatCell class]) bundle:[NSBundle mainBundle]];
    UIView *contentView = [nib instantiateWithOwner:self options:nil].firstObject;
    [self.contentView addSubview:contentView];
    
    // 添加約束吭服,讓內(nèi)容充滿cell
    contentView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[contentView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(contentView)]];
    [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[contentView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(contentView)]];
}
@end

子類cell實現(xiàn)(這里以文字消息cell為例嚷堡,其他子類cell中代碼一模一樣)

@implementation TextChatCell
#pragma mark - Life Circle
- (void)loadViewFromXib
{
    // 加載父類xib中內(nèi)容
    [super loadViewFromXib];
    
    // 加載當前類xib中內(nèi)容
    [super loadChildViewFromXib];
}

/// 加載子類xib
- (void)loadChildViewFromXib
{
    // 加載子類xib,將差異化的視圖元素艇棕,加載到父視圖指定區(qū)域內(nèi)
    UINib *nib = [UINib nibWithNibName:NSStringFromClass([self class]) bundle:[NSBundle mainBundle]];
    UIView *contentView = [nib instantiateWithOwner:self options:nil].firstObject;
    [self.viewContent addSubview:contentView];
    
    // 添加約束蝌戒,讓內(nèi)容充滿cell
    contentView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[contentView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(contentView)]];
    [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[contentView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(contentView)]];
    
    // 做一些處理,其實這些也可以在xib直接設置好
    contentView.backgroundColor = [UIColor clearColor];
    self.viewContent.backgroundColor = [UIColor clearColor];
}
@end


因為每個子類中的loadChildViewFromXib方法實現(xiàn)都是一樣的沼琉,所以我們可以把這個方法提取出來北苟,放到父類中,子類直接調(diào)用打瘪,免去了每次都在子類中實現(xiàn)一遍

@implementation BaseChatCell
#pragma mark - Life Circle
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        [self loadViewFromXib];
    }
    return self;
}

/// 加載父類xib
- (void)loadViewFromXib
{
    UINib *nib = [UINib nibWithNibName:NSStringFromClass([BaseChatCell class]) bundle:[NSBundle mainBundle]];
    UIView *contentView = [nib instantiateWithOwner:self options:nil].firstObject;
    [self.contentView addSubview:contentView];
    
    // 添加約束友鼻,讓內(nèi)容充滿cell
    contentView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[contentView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(contentView)]];
    [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[contentView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(contentView)]];
}

/// 加載子類xib
- (void)loadChildViewFromXib
{
    // 加載子類xib傻昙,將差異化的視圖元素,加載到父視圖指定區(qū)域內(nèi)
    UINib *nib = [UINib nibWithNibName:NSStringFromClass([self class]) bundle:[NSBundle mainBundle]];
    UIView *contentView = [nib instantiateWithOwner:self options:nil].firstObject;
    [self.viewContent addSubview:contentView];
    
    // 添加約束彩扔,讓內(nèi)容充滿cell
    contentView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[contentView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(contentView)]];
    [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[contentView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(contentView)]];
    
    // 做一些處理妆档,其實這些也可以在xib直接設置好
    contentView.backgroundColor = [UIColor clearColor];
    self.viewContent.backgroundColor = [UIColor clearColor];
}
@end


@implementation TextChatCell
#pragma mark - Life Circle
- (void)loadViewFromXib
{
    // 加載父類xib中內(nèi)容
    [super loadViewFromXib];
    
    // 加載當前類xib中內(nèi)容
    [super loadChildViewFromXib];
}
@end


方案二:

父類沒有xib,每個子類xib虫碉,都是完整的內(nèi)容贾惦。(xib全部綁定的是頂級對象view)
子類通過registerNib注冊cell。

方案二xib.png

先注冊cell(這里注冊方式與方案一相反敦捧,要用registerNib的方式注冊须板,不能是registerClass

// 首先使用registerNib的方式注冊子類cell
[_tableView registerNib:[UINib nibWithNibName:NSStringFromClass([CustomerServiceTextCell class]) bundle:nil] forCellReuseIdentifier:NSStringFromClass([CustomerServiceTextCell class])];
    [_tableView registerNib:[UINib nibWithNibName:NSStringFromClass([CustomerServiceImageCell class]) bundle:nil] forCellReuseIdentifier:NSStringFromClass([CustomerServiceImageCell class])];

// 然后就可以用重用獲取cell了
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CustomerServiceTextCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([CustomerServiceTextCell class]) forIndexPath:indexPath];
}

然后就沒了??,方案二的沒有代碼上需要處理的兢卵,唯一要注意的點就是子類xib中共同的視圖元素习瑰,都可以連線到父類中,然后就在父類的代碼中處理公共的邏輯秽荤。

結論:
方案一:方便以后的拓展甜奄、易維護。雖然理解上可能比方案二稍稍麻煩點窃款,但多用幾次就好了贺嫂,推薦。
方案二:很來很簡單雁乡,適合剛接觸xib的人,但強烈不推薦糜俗。你日后維護起來會很崩潰的踱稍,只要公共部分有改動,就需要去每個子類xib中修改悠抹,而且這里面一堆約束珠月,很容易出錯的。

當然還有其他組合出來的方案楔敌,但就我所知的情況中,和這兩種都是大同小異,有興趣的可以研究下其他的搭配脑又。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末貌虾,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子勺卢,更是在濱河造成了極大的恐慌伙判,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件黑忱,死亡現(xiàn)場離奇詭異宴抚,居然都是意外死亡勒魔,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門菇曲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來冠绢,“玉大人,你說我怎么就攤上這事常潮〉苷停” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵蕊玷,是天一觀的道長邮利。 經(jīng)常有香客問我,道長垃帅,這世上最難降的妖魔是什么延届? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮贸诚,結果婚禮上方庭,老公的妹妹穿的比我還像新娘。我一直安慰自己酱固,他們只是感情好械念,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著运悲,像睡著了一般龄减。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上班眯,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天希停,我揣著相機與錄音,去河邊找鬼署隘。 笑死宠能,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的磁餐。 我是一名探鬼主播违崇,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼诊霹!你這毒婦竟也來了羞延?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤脾还,失蹤者是張志新(化名)和其女友劉穎肴楷,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體荠呐,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡赛蔫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年砂客,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片呵恢。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡鞠值,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出渗钉,到底是詐尸還是另有隱情彤恶,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布鳄橘,位于F島的核電站声离,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏瘫怜。R本人自食惡果不足惜术徊,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鲸湃。 院中可真熱鬧赠涮,春花似錦、人聲如沸暗挑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽炸裆。三九已至垃它,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間烹看,已是汗流浹背嗤瞎。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留听系,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓虹菲,卻偏偏與公主長得像靠胜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子毕源,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,104評論 25 707
  • 環(huán)境 xcode8.3浪漠,swift3,rxswift 3.6 前言 在做新項目并且對swift不是那么了解的時候霎褐,...
    liuvbill閱讀 1,130評論 0 0
  • 雞都叫了…
    象歪歪閱讀 285評論 0 0
  • 今天兒子新學校報到址愿。沒到五點半,孩子就醒來冻璃,看來有點小激動呀响谓!平時大大咧咧的兒子损合,還是很在意。感賞兒子自覺性越來越...
    玫瑰鏗鏘閱讀 189評論 0 3
  • 連載 《它》 01 一天《它》 02 又一天《它》 03 與兩腳怪獸們的一天(上)《它》 04 與兩腳怪...
    朽人不入流閱讀 183評論 0 0