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

NSArray *views = [[NSBundle mainBundle] loadNibNamed:@"View1"owner:nil options:nil];//&1

UIView *v = [views lastObject];

CGRect r = v.frame;

r.origin.y += 80;

v.frame = r;

[self.view addSubview:v];

&1这行代码就是加载View1.xib, 然后将xib中的UIView实保存到views中, 由于xib中我们只拖入了一个view, 所以这儿lastObject就返回这个view的实例,这样便加载了xib中的UIView. 接着将这个UIView addSubview到其它view上,运行效果如图:

2. 通过Owner建立变量关联

首先我们为ViewController创建一个IBOutlet属性:

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

接着同上面介绍的一样创建一个View2.xib, 如下图:

File’s Owner中,我们设为ViewController, 这样我们就可以与实例变量_referencedView建立关联了,如图:

接着在viewDidLoad中,在刚才加入的代码下面添加如下代码:

// 2

[[NSBundle mainBundle] loadNibNamed:@"View2"owner:self options:nil];

r = _referencedView.frame;

r.origin.y = v.frame.size.height + v.frame.origin.y;

_referencedView.frame = r;

[self.view addSubview:_referencedView];

与//1中的代码有点类似,只不过owner属性为self了。这样一来,loadNibNamed后,就会实例化与之关联的变量_referencedView, 运行程序你将会看到效果:

3.Class Owner建立变量关联

其实这个原理与上面2说的一样的,只不过这儿我们特别定义一个class来作为xib的Owner, 要所有需要关系的view都可以声明在这个Owner中,这样方便代码管理与维护。

这里我们声明一个NSObject的子类FileOwner, 然后再在FileOnwer中声明IBOutLet的关联变量:

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

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

接着在viewDidLoad结尾处添加以下代码:

// 3

FileOwner *owner = [FileOwnernew];

[[NSBundle mainBundle] loadNibNamed:@"View3"owner:owner options:nil];

r = owner.view.frame;

r.origin.y = _referencedView.frame.origin.y + _referencedView.frame.size.height;

owner.view.frame = r;

[self.view addSubview:owner.view];

运行效果:

4. 引入UIView Category

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

+(id)loadFromNibNamed:(NSString*) nibName {

return[FileOwner viewFromNibNamed:nibName];

}

其中FileOwner的class 方法:

+(id)viewFromNibNamed:(NSString*) nibName {

FileOwner *owner = [selfnew];

[[NSBundle mainBundle] loadNibNamed:nibName owner:owner options:nil];

returnowner.view;

}

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

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

接着在viewDidLoad尾添加代码:

// 4

UIView *v4 = [UIView loadFromNibNamed:@"View4"];

r = v4.frame;

r.origin.y = owner.view.frame.origin.y + owner.view.frame.size.height;

v4.frame = r;

[self.view addSubview:v4];

运行效果:

5. 自定义UIView类

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

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

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

+(id)loadFromNib {

return[self loadFromNibNamed:NSStringFromClass(self)];

}

在viewDidLoad尾加入代码:

// 5

View5 *v5 = [View5 loadFromNib];

r = v5.frame;

r.origin.y = v4.frame.origin.y + v4.frame.size.height;

v5.frame = r;

[self.view addSubview:v5];

动行效果:

6.设置Onwer为UIViewController

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

接着在viewDidLoad中添加代码:

// 6

UIView *v6 = [[UIViewController alloc] initWithNibName:@"View6"bundle:nil].view;

r = v6.frame;

r.origin.y = v5.frame.origin.y + v5.frame.size.height;

v6.frame = r;

[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的时候会触发一些方法,如:

- (id)initWithCoder:(NSCoder *)aDecoder

- (id)awakeAfterUsingCoder:(NSCoder*)aDecoder

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

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

#import "SubView.h"

#include "UIView+Ext.h"

@implementation SubView

- (id)initWithFrame:(CGRect)frame

{

self = [superinitWithFrame:frame];

if(self) {

// Initialization code

}

returnself;

}

- (id) awakeAfterUsingCoder:(NSCoder*)aDecoder {

BOOL theThingThatGotLoadedWasJustAPlaceholder = ([[self subviews] count] == 0);

if(theThingThatGotLoadedWasJustAPlaceholder) {

SubView* theRealThing = [[selfclass] loadFromNibNoOwner];

// pass properties through

[self copyUIPropertiesTo:theRealThing];

//auto layout

self.translatesAutoresizingMaskIntoConstraints = NO;

theRealThing.translatesAutoresizingMaskIntoConstraints = NO;

returntheRealThing;

}

returnself;

}

-(void) copyUIPropertiesTo:(UIView *)view

{

// reflection did not work to get those lists, so I hardcoded them

// any suggestions are welcome here

NSArray *properties =

[NSArray arrayWithObjects: @"frame",@"bounds", @"center", @"transform", @"contentScaleFactor", @"multipleTouchEnabled", @"exclusiveTouch", @"autoresizesSubviews", @"autoresizingMask", @"clipsToBounds", @"backgroundColor", @"alpha", @"opaque", @"clearsContextBeforeDrawing", @"hidden", @"contentMode", @"contentStretch", nil];

// some getters have 'is' prefix

NSArray *getters =

[NSArray arrayWithObjects: @"frame", @"bounds", @"center", @"transform", @"contentScaleFactor", @"isMultipleTouchEnabled", @"isExclusiveTouch", @"autoresizesSubviews", @"autoresizingMask", @"clipsToBounds", @"backgroundColor", @"alpha", @"isOpaque", @"clearsContextBeforeDrawing", @"isHidden", @"contentMode", @"contentStretch", nil];

for(inti=0; i<[properties count]; i++)

{

NSString * propertyName = [properties objectAtIndex:i];

NSString * getter = [getters objectAtIndex:i];

SEL getPropertySelector = NSSelectorFromString(getter);

NSString *setterSelectorName =

[propertyName stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:[[propertyName substringToIndex:1] capitalizedString]];

setterSelectorName = [NSString stringWithFormat:@"set%@:", setterSelectorName];

SEL setPropertySelector = NSSelectorFromString(setterSelectorName);

if([self respondsToSelector:getPropertySelector] && [view respondsToSelector:setPropertySelector])

{

NSObject * propertyValue = [self valueForKey:propertyName];

[view setValue:propertyValue forKey:propertyName];

}

}

}

@end

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

我的xib信息是这样的:

一切就绪后,运行:

xib可以快速布署UI, 可以提高开发速度哦。


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354

推荐阅读更多精彩内容