1. 程式人生 > >iOS開發之xib技巧介紹

iOS開發之xib技巧介紹

iOS開發的這些年裡,有的人用程式碼建立UI,有的人用xib建立UI。到底是用xib還是程式碼來建立UI,這個問題以前也有過很多爭論,我只想說一點,各有各的優點。如果能夠將兩者融合貫通,那將是更有優勢。筆者開發過程中,UI能用xib就儘量用xib(能用storyboard就用storyboard, 一個storyboard裡最好別裝太多的UIViewController,這在結隊開發中將不利)。本文主要介紹使用xib的一些技術,即在xib中佈局UI,然後xib與code相結合,快速UI開發介紹。本文主要講解的也就是載入xib的技術。

還是老方法,用程式碼說話,首先建立一個Single Page工程,命名為:LoadNibViewDemo。

1.直接載入xib中的UIView

建立一個View1.xib, 隨便設一個背景色,加一個標識UILabel, 這樣好知道是這個view是哪一個view. 你可以在這個view上加作意的subview,我只是說明原理,所以這兒並沒有加作何subview. 最終我的View1如下圖:

由於View1會放到其它View上作為subview,所以這兒size是Freeform, Status Bar是:None。

將下面程式碼放到viewDidLoad中:

  1. //1
  2. NSArray *views = [[NSBundle mainBundle] loadNibNamed:@"View1" owner:nil options:nil]; 
    //&1
  3. UIView *v = [views lastObject]; 
  4. CGRect r = v.frame; 
  5. r.origin.y += 80; 
  6. v.frame = r; 
  7. [self.view addSubview:v]; 

&1這行程式碼就是載入View1.xib, 然後將xib中的UIView實儲存到views中, 由於xib中我們只拖入了一個view, 所以這兒lastObject就返回這個view的例項,這樣便載入了xib中的UIView. 接著將這個UIView addSubview到其它view上,執行效果如圖: 

2. 通過Owner建立變數關聯

首先我們為ViewController建立一個IBOutlet屬性:

  1. @property (nonatomic, weak) IBOutlet UIView *referencedView; 

接著同上面介紹的一樣建立一個View2.xib, 如下圖:

File’s Owner中,我們設為ViewController, 這樣我們就可以與例項變數_referencedView建立關聯了,如圖:

接著在viewDidLoad中,在剛才加入的程式碼下面新增如下程式碼:

  1. // 2
  2. [[NSBundle mainBundle] loadNibNamed:@"View2" owner:self options:nil]; 
  3. r = _referencedView.frame; 
  4. r.origin.y = v.frame.size.height + v.frame.origin.y; 
  5. _referencedView.frame = r; 
  6. [self.view addSubview:_referencedView]; 

與//1中的程式碼有點類似,只不過owner屬性為self了。這樣一來,loadNibNamed後,就會例項化與之關聯的變數_referencedView, 執行程式你將會看到效果: 

3.Class Owner建立變數關聯

其實這個原理與上面2說的一樣的,只不過這兒我們特別定義一個class來作為xib的Owner, 要所有需要關係的view都可以宣告在這個Owner中,這樣方便程式碼管理與維護。

這裡我們宣告一個NSObject的子類FileOwner, 然後再在FileOnwer中宣告IBOutLet的關聯變數:

  1. @property (nonatomic, weak) IBOutlet UIView *view; 

同理建立一個View3.xib, File’s Owner設為FileOwner, 並建立view關聯: 

接著在viewDidLoad結尾處新增以下程式碼:

  1. // 3
  2. FileOwner *owner = [FileOwner new]; 
  3. [[NSBundle mainBundle] loadNibNamed:@"View3" owner:owner options:nil]; 
  4. r = owner.view.frame; 
  5. r.origin.y = _referencedView.frame.origin.y + _referencedView.frame.size.height; 
  6. owner.view.frame = r; 
  7. [self.view addSubview:owner.view]; 

執行效果: 

  4. 引入UIView Category

為了程式碼簡單,我們增加一個UIView Category方法:

  1. +(id)loadFromNibNamed:(NSString*) nibName { 
  2.     return [FileOwner viewFromNibNamed:nibName]; 

其中FileOwner的class 方法:

  1. +(id)viewFromNibNamed:(NSString*) nibName { 
  2.     FileOwner *owner = [self new]; 
  3.     [[NSBundle mainBundle] loadNibNamed:nibName owner:owner options:nil]; 
  4.     return owner.view; 

這樣載入xib的程式碼就會變得更簡單。

同理,我們建立一個View4.xib, File’s Owner設為FileOwner, 並建立view關聯:

接著在viewDidLoad尾新增程式碼:

  1. // 4
  2. UIView *v4 = [UIView loadFromNibNamed:@"View4"]; 
  3. r = v4.frame; 
  4. r.origin.y = owner.view.frame.origin.y + owner.view.frame.size.height; 
  5. v4.frame = r; 
  6. [self.view addSubview:v4]; 

執行效果:

 

5. 自定義UIView類

在4Category的基礎上,我們再引入自定義UIView類,並在xib中與之關聯。首先我們建立一個UIView字類UIView5。

接著,我們建立一個View5.xib, File’s Owner設為FileOwner, 並建立view關聯: 

接著增加一個UIView的Category方法:

  1. +(id)loadFromNib { 
  2.     return [self loadFromNibNamed:NSStringFromClass(self)]; 

在viewDidLoad尾加入程式碼:

  1. // 5
  2. View5 *v5 = [View5 loadFromNib]; 
  3. r = v5.frame; 
  4. r.origin.y = v4.frame.origin.y + v4.frame.size.height; 
  5. v5.frame = r; 
  6. [self.view addSubview:v5]; 

動行效果:

 

6.設定Onwer為UIViewController

首先,我們建立一個View6.xib, File’s Owner設為UIViewController. 這樣UIViewController的view屬性關聯我們xib中的UIView  

接著在viewDidLoad中新增程式碼:

  1. // 6
  2. UIView *v6 = [[UIViewController alloc] initWithNibName:@"View6" bundle:nil].view; 
  3. r = v6.frame; 
  4. r.origin.y = v5.frame.origin.y + v5.frame.size.height; 
  5. v6.frame = r; 
  6. [self.view addSubview:v6]; 

動行效果: 

 

說了這麼多,是時候做一下總結了,其實其本是兩個方法,一個是沒有File’s Onwer直接載入xib中的UIView,二是通過File’s Onwer關聯變數載入xib中的UIView。 然後就是一些Category提供簡單介面而已。大家可以再細細品味一下上面所介紹的內容。

大家可以看我原始碼中UIView+Ext的Category方法中還提供了一個方法:+ (id)loadFromNibNoOwner;它應是方法5與方法1的組合,在此我就不細說了。 都是由上面兩個基本方法演變出來的。

7. xib link xib

大家有沒有想過在xib中link其它xib? 很可惜蘋果不支援這個功能。但是我們可以通過一點技巧實現這個功能。下而我就簡單介紹一下。

先說一下原理,載入xib的UIView,如果這個UIView是自定義的UIView(即xib中關聯了UIView的子類),如下圖: 

那麼在載入顯示這個view的時候會觸發一些方法,如:

  1. - (id)initWithCoder:(NSCoder *)aDecoder 
  2. - (id)awakeAfterUsingCoder:(NSCoder*)aDecoder 

我們就在這兒作些文章,在這兒用前面介紹的方法載入想要的的xib中UI例項替換掉原來返回的例項。

首先我寫了一個UIView的了類SubView,程式碼很容易理解:

  1. #import "SubView.h"
  2. #include "UIView+Ext.h"
  3. @implementation SubView 
  4. - (id)initWithFrame:(CGRect)frame 
  5.     self = [super initWithFrame:frame]; 
  6.     if (self) { 
  7.         // Initialization code
  8.     } 
  9.     return self; 
  10. - (id) awakeAfterUsingCoder:(NSCoder*)aDecoder { 
  11.     BOOL theThingThatGotLoadedWasJustAPlaceholder = ([[self subviews] count] == 0); 
  12.     if (theThingThatGotLoadedWasJustAPlaceholder) { 
  13.         SubView* theRealThing = [[self class] loadFromNibNoOwner]; 
  14.         // pass properties through
  15.         [self copyUIPropertiesTo:theRealThing]; 
  16.         //auto layout
  17.         self.translatesAutoresizingMaskIntoConstraints = NO; 
  18.         theRealThing.translatesAutoresizingMaskIntoConstraints = NO; 
  19.         return theRealThing; 
  20.     } 
  21.     return self; 
  22. -(void) copyUIPropertiesTo:(UIView *)view 
  23.     // reflection did not work to get those lists, so I hardcoded them
  24.     // any suggestions are welcome here
  25.     NSArray *properties = 
  26.     [NSArray arrayWithObjects: @"frame",@"bounds", @"center", @"transform", @"contentScaleFactor", @"multipleTouchEnabled", @"exclusiveTouch", @"autoresizesSubviews", @"autoresizingMask", @"clipsToBounds", @"backgroundColor", @"alpha", @"opaque", @"clearsContextBeforeDrawing", @"hidden", @"contentMode", @"contentStretch", nil]; 
  27.     // some getters have 'is' prefix
  28.     NSArray *getters = 
  29.     [NSArray arrayWithObjects: @"frame", @"bounds", @"center", @"transform", @"contentScaleFactor", @"isMultipleTouchEnabled", @"isExclusiveTouch", @"autoresizesSubviews", @"autoresizingMask", @"clipsToBounds", @"backgroundColor", @"alpha", @"isOpaque", @"clearsContextBeforeDrawing", @"isHidden", @"contentMode", @"contentStretch", nil]; 
  30.     for (int i=0; i<[properties count]; i++) 
  31.     { 
  32.         NSString * propertyName = [properties objectAtIndex:i]; 
  33.         NSString * getter = [getters objectAtIndex:i]; 
  34.         SEL getPropertySelector = NSSelectorFromString(getter); 
  35.         NSString *setterSelectorName = 
  36.         [propertyName stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:[[propertyName substringToIndex:1] capitalizedString]]; 
  37.         setterSelectorName = [NSString stringWithFormat:@"set%@:", setterSelectorName]; 
  38.         SEL setPropertySelector = NSSelectorFromString(setterSelectorName); 
  39.         if ([self respondsToSelector:getPropertySelector] && [view respondsToSelector:setPropertySelector]) 
  40.         { 
  41.             NSObject * propertyValue = [self valueForKey:propertyName]; 
  42.             [view setValue:propertyValue forKey:propertyName]; 
  43.         } 
  44.     }     
  45. @end 

建立一個EmbeddedView.xib,我們想在其它xib中直接link這個EmbeddedView.xib, 還需要建立一個SubView的了類EmbeddedView。

我的xib資訊是這樣的:

一切就緒後,執行: 

xib可以快速佈署UI, 可以提高開發速度哦。 隨便在此預告一下下一篇教程的內容:多Storyboard協作開發。