iOS开发之xib技巧介绍

转自 Haven's blog

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.  
  3. NSArray *views = [[NSBundle mainBundle] loadNibNamed:@ "View1"  owner:nil options:nil];  //&1  
  4.  
  5. UIView *v = [views lastObject]; 
  6.  
  7. CGRect r = v.frame; 
  8.  
  9. r.origin.y += 80; 
  10.  
  11. v.frame = r; 
  12.  
  13. [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.  
  3. [[NSBundle mainBundle] loadNibNamed:@ "View2"  owner:self options:nil]; 
  4.  
  5. r = _referencedView.frame; 
  6.  
  7. r.origin.y = v.frame.size.height + v.frame.origin.y; 
  8.  
  9. _referencedView.frame = r; 
  10.  
  11. [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.  
  3. FileOwner *owner = [FileOwner  new ]; 
  4.  
  5. [[NSBundle mainBundle] loadNibNamed:@ "View3"  owner:owner options:nil]; 
  6.  
  7. r = owner.view.frame; 
  8.  
  9. r.origin.y = _referencedView.frame.origin.y + _referencedView.frame.size.height; 
  10.  
  11. owner.view.frame = r; 
  12.  
  13. [self.view addSubview:owner.view]; 

运行效果: 

 

4. 引入UIView Category

为了代码简单,我们增加一个UIView Category方法:

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

其中FileOwner的class 方法:

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

这样加载xib的代码就会变得更简单。

同理,我们创建一个View4.xib, File’s Owner设为FileOwner, 并建立view关联:

接着在viewDidLoad尾添加代码:

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

运行效果:

 

5. 自定义UIView类

在4Category的基础上,我们再引入自定义UIView类,并在xib中与之关联。首先我们创建一个UIView字类UIView5。

接着,我们创建一个View5.xib, File’s Owner设为FileOwner, 并建立view关联: 

接着增加一个UIView的Category方法:

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

在viewDidLoad尾加入代码:

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

动行效果:

 

6.设置Onwer为UIViewController

首先,我们创建一个View6.xib, File’s Owner设为UIViewController. 这样UIViewController的view属性关联我们xib中的UIView  

接着在viewDidLoad中添加代码:

  1. // 6  
  2.  
  3. UIView *v6 = [[UIViewController alloc] initWithNibName:@ "View6"  bundle:nil].view; 
  4.  
  5. r = v6.frame; 
  6.  
  7. r.origin.y = v5.frame.origin.y + v5.frame.size.height; 
  8.  
  9. v6.frame = r; 
  10.  
  11. [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.  
  3. - (id)awakeAfterUsingCoder:(NSCoder*)aDecoder 

我们就在这儿作些文章,在这儿用前面介绍的方法加载想要的的xib中UI实例替换掉原来返回的实例。

首先我写了一个UIView的了类SubView,代码很容易理解:

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

创建一个EmbeddedView.xib,我们想在其它xib中直接link这个EmbeddedView.xib, 还需要创建一个SubView的了类EmbeddedView。

我的xib信息是这样的:

一切就绪后,运行: 

xib可以快速布署UI, 可以提高开发速度哦。 随便在此预告一下下一篇教程的内容:多Storyboard协作开发。

终于这个教程写完了,完整的Demo可以在此下载 :LoadNibViewDemo 。希望大家多多支持,你们的支持将是我源源不断的动力。

我来评几句
登录后评论

已发表评论数()