xib的動態橋接

本文原文發表自個人【自建博客】,cnblogs同步發表,格式未經調整,內容以原博客爲準

 

我是前言

我的很主張使用Interface Builder(如下都簡稱IB)來構建程序UI,包括storyboardxib,相比代碼更可視和易於修改,尤爲在使用AutoLayout的時候,一目瞭然。
但用了這麼久IB以後發現一個很大的槽點,就是IB間很難嵌套混用,好比一個xib中的view是另外一個xib的子view,或者一個storyboard中兩個vc都用到了一個xib構建的view等。解決方法通常是代碼手動拼接,這就形成了比較混亂的狀況。
本文將嘗試解決這個問題,實現xib的動態橋接,並提供一個支持cocoapods的開源工具類供方便使用。html

一張圖頂十句話:

實現效果:


黑魔法方法

實現這個功能的關鍵在於:在ib加載的某個時刻將placeholder的view動態替換成從xib加載的view,下面的方法就能夠作到:ios

1
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder NS_REPLACES_RECEIVER; 

這個方法不多用到,在NSObject (NSCoderMethods)中定義,由NSCoder在decode過程當中調用(於-initWithCoder:以後),因此說就算從文件裏decode出來的對象也會走這個方法。
方法後面有NS_REPLACES_RECEIVER這個宏:git

1
#define NS_REPLACES_RECEIVER __attribute__((ns_consumes_self)) NS_RETURNS_RETAINED 


在clang的文檔中能夠找到對這個編譯器屬性的介紹

> One use of this attribute is declare your own init-like methods that do not follow the standard Cocoa naming conventions.

因此這個宏主要爲了給編譯器標識出這個方法能夠像self = [super init]同樣使用,並做出合理的內存管理。
So,這個方法提供了一個機會,能夠將decode出來的對象替換成另外一個對象


動態橋接流程

github

1
2
3
4
5
6
7
8
9
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder
{
    self = [super awakeAfterUsingCoder:aDecoder]; // 0. 判斷是否要進行替換 // 1. 根據self.class從xib建立真正的view // 2. 將placeholder的屬性、autolayout等替換到真正的view上 // 3. return 真正的view } 

流程不難理解,就是有2個小難點:函數

  • 步驟1從xib建立真正的view時也會調用這個方法,會形成遞歸,如何判斷
  • 遷移AutoLayoutConstrains

解決遞歸問題

這個topic全網可能就《這篇文章》有寫,本文也是從它發起的,可是發現它的方法並不能解決全部問題(尤爲是用storyboard加載xib時),因此換了個思路,採起了設置標誌位的方式避免遞歸調用:工具

1
2
3
4
5
6
7
8
9
10
11
12
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder
{
    self = [super awakeAfterUsingCoder:aDecoder]; if (這個類的Loading標誌位 -> NO) { Loading標誌位 -> YES 從xib加載真實的View (這裏會遞歸調用這個函數) return 真實View } Loading標誌位 -> NO return self } 

方法有點土,可是有效了,源代碼文章後面會給地址。ui

遷移AutoLayoutConstrains

因爲IB在加載AutoLayoutConstrains時的順序是先加載子View內部的約束,後加載父View上的約束,而咱們替換placeholder的時機是:this

  1. placehodler view被建立(只帶width,height的自身約束)
  2. 真正的view被從xib動態加載(帶其子view的全部約束)
  3. placeholder被替換成真的view
  4. placeholder view在其父View(一直到父父父…View)的約束被建立

因此說,遷移AutoLayout時,只須要把placeholder view的自身約束copy到真實View上就行了(停頓10s感覺下)
代碼以下:spa

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (void)replaceAutolayoutConstrainsFromView:(UIView *)placeholderView toView:(UIView *)realView
{
    for (NSLayoutConstraint *constraint in placeholderView.constraints) { NSLayoutConstraint* newConstraint = [NSLayoutConstraint constraintWithItem:realView attribute:constraint.firstAttribute relatedBy:constraint.relation toItem:nil // Only first item attribute:constraint.secondAttribute multiplier:constraint.multiplier constant:constraint.constant]; newConstraint.shouldBeArchived = constraint.shouldBeArchived; newConstraint.priority = constraint.priority; [realView addConstraint:newConstraint]; } } 


One more thing,保證AutoLayout生效還要加上下面這句話:

code

1
realView.translatesAutoresizingMaskIntoConstraints = NO; 

開源項目XXNibBridge

光說方案不給源碼仍是不地道的,demo放到了個人github上面的XXNibBridge項目,回顧一下上面的關係圖:

不得不提到IB命名約定的最佳實踐方案:

將類名做爲Cell或者VC的Reusable Identifier
ReuseIdentifier一直比較蛋疼,我通常將Cell的類名做爲ReuseIdentifier(固然,大多數狀況咱們都會子類化Cell的),寫法如:

1
[self.tableView registerClass:[XXSarkCell class] forCellReuseIdentifier:NSStringFromClass([XXSarkCell class])]; 


dequeueCell的時候同理,這樣的好處在於省去了起名的噁心、經過ReuseId能夠直接找到Cell類、同時重構Cell類名時ReuseId也不用去再改。

View的xib與View的類名同名 同理

實現了橋接Xib的功能的同時,也簡單實現了這個命名約定:

1
2
3
4
5
// XXNibBridge.h
+ (NSString *)xx_nibID; // 類名 + (UINib *)xx_nib; // 返回類名對應nib + (id)xx_loadFromNib; // 對應nib的類對象 + (id/*UIViewController*/)xx_loadFromStoryboardNamed:(NSString *)name; // 返回類名對應的vc 


因此以後的代碼能夠這麼寫:

1
[tableView registerNib:[XXSarkView xx_nib] forCellReuseIdentifier:[XXSarkView xx_nibID]]; 

XXNibBridge的使用

Cocoapods安裝

1
pod 'XXNibBridge', :git => 'https://github.com/sunnyxx/XXNibBridge.git' 

對於要支持Bridge的類,重載下面的方法:

1
2
3
4
5
6
7
#import "XXNibBridge.h" @implementation XXDogeView + (BOOL)xx_shouldApplyNibBridging { return YES; } @end 

在父的Xib或Storyboard中拖個UIView進來做爲Placeholder,設置爲真實Nib的類

保證真實Nib的類名和Nib名相同,記得在Nib中設好Class

Done.


References

http://blog.yangmeyer.de/blog/2012/07/09/an-update-on-nested-nib-loading
http://stackoverflow.com/questions/19816703/replacing-nsview-while-keeping-autolayout-constraints
http://clang-analyzer.llvm.org/annotations.html#attr_ns_consumes_self


原創文章,轉載請註明源地址,blog.sunnyxx.com

相關文章
相關標籤/搜索