一天一点xib:8高冷的xib

引言

学到这里,xib给我带来的帮助已经很大了,最大的莫过于UI控件的创建、属性的赋值再也不用写代码,就UI开发来说大大减少了代码,而且设置delegate、addTarget、属性声明等也不用自己写代码了。SB的segue使我们的页面跳转也不需要写代码了,还有强大的cell功能更是在开发中起到了很大的作用,除此之外,xib还有一些高冷功能,这些功能有些很少有人使用,你在网上也几乎看不到关于它的文章,但是它们确实是很好用,下面我们就来一起看看。

是时候总结一下了

当我们在代码中打出IB的时候,代码提示会是这样的:

在开始介绍一些高冷用法之前,我们先总结一下它们:

IBAction与IBOutlet

这是我们最常接触的两个,大家对它们已经有了很好的认识,这里只简单的说一下。

对于一个类来说,方法和属性(在这里属性与字段合在一起表示一个概念)是最重要的两个要素,而IBAction与IBOutlet就是分别标识方法与属性的,它们标识着由它们修饰的方法和属性是来自xib的,我猜它们是给编译器看的。

IBInspectable

在OC中使用IBInspectable,在swift中使用@IBInspectable

它是xcode6引入的新功能,它修饰的属性或者实例变量,会显示在xib中的属性栏中(Show the Attributes inspector),我们之前讲的东西都是xib是如何影响代码的,而IBInspectable是可以用代码影响xib的,可能我的表述不是很正确,还是看一个具体例子吧。

@interface ViewController : UIViewController

//gj_testFlag用IBInspectable修饰后,就能在xib中看到这个属性了,当然也可以用xib进行赋值了
@property (assign, nonatomic) IBInspectable BOOL gj_testFlag;

@end

这个例子这是为了说明IBInspectable的用法,具体应用没有太大意义,但是如果我们会灵活运用IBInspectable就会带来极大的方便,其实xib的好多用法都有着意想不到的效果,大家可以在各个情况下去尝试,这是一件很酷的事。

IB_DESIGNABLE

在OC中将IB_DESIGNABLE写在@implementation前,在swift中将@IBDesignable写在class前

它也是xcode6引入的新功能,它的作用是可以在不运行的情况下把你的代码显示在xib或SB文件中。

两点说明:

1.这是一个针对UI显示的功能,所以只能是在UIView及其子类或者NSView及其子类上生效。

2.要想使IBDesignable起作用必须把代码写在drawRect里才能显示,同样的代码,我写在了awakeFromNib里就不会再xib中看出效果,只有写在了drawRect才可以。

举个例子:

我们建一个工程,新建一个TestView类继承自UIVIew,在Main.storyboard里拖一个View,class设置为TestView,背景设置成灰色。

然后写代码:

IB_DESIGNABLE
@implementation TestView

- (void)drawRect:(CGRect)rect {
    UIBezierPath *firtPath = 
    [UIBezierPath bezierPathWithOvalInRect:CGRectMake(10, 10, 180, 180)];
    CAShapeLayer *shapeL = [CAShapeLayer layer];
    shapeL.lineWidth = 20;
    shapeL.path =firtPath.CGPath;
    shapeL.strokeStart = 0;
    shapeL.strokeEnd = 1;
    shapeL.strokeColor = [UIColor yellowColor].CGColor;
    shapeL.fillColor = [UIColor clearColor].CGColor;
    [self.layer addSublayer:shapeL];
    self.layer.cornerRadius = 30;
    self.layer.masksToBounds = YES;
}

@end

此时再看Main.storyboard中View的变化:

我们的IB_DESIGNABLE功能已经生效了

我们把下面的代码从drawRect:中剪切到awakeFromNib函数中会发现,圆角不见了,这里要特别注意。

self.layer.cornerRadius = 30;
self.layer.masksToBounds = YES;

IBOutletCollection(ClassName):

将基于IBOutlet创建的对象放在一个NSarray里。
@property (strong, nonatomic) IBOutletCollection(UIView) NSArray *testViewArr;
创建了一个array,里面放的是用IBOutlet创建的UIView.

注意最好用strong进行修饰,而且如果你声明的不是NSArray,即便是UIColor,系统也不会报错,你打印这个color发现,系统用的还是NSArray。这个array的顺序是连线时候的顺序,但是不排除不同版本的xcode会改变这个顺序,所以最好不要依赖这个顺序。

也可以像拖IBOutlet那样创建:

用xib给控件添加手势

Files Owner的应用举例

有这样一个场景,VC中有一个textfield要设置inputAccessoryView属性,该属性的view显示起来很复杂,有多个按钮,每个按钮对应不同的事件。

一般的做法是用代码写一个这样的view赋值给inputAccessoryView属性,其实这个例子可以用xib实现的更优雅,不用写代码就可以完成(当然点击每个按钮后的事件处理代码是要自己写的)。

例子中要考虑的重点是:
如果创建了一个AccessoryView.xib去拖出这样一个view,虽然不用“画”UI了,但是我们要建一个AccessoryView.h、AccessoryView.m类去与xib文件对应,在AccessoryView.m中把它上面的按钮事件记录下来,一旦触发事件,要通过delegate或通知等其他形式把事件从AccessoryView类传递给VC类,这样使事情更加的麻烦了,如何解决?

有人会想:创建AccessoryView.h、AccessoryView.m是没有必要的,因为他们除了传递事件,根本没做任何事情,这样的话就不创建他们,只有AccessoryView.xib文件,然后把xib中的按钮分别拖动到VC类中建立起IBAction的“连线”关系,事情就搞定了。

这个思路很好,但是我们会发现,并不能实现AccessoryView.xib与VC中的“连线”,因为VC类根本不认识这个xib,因此该VC是不允许这个xib通过“连线”向它内部添加代码的,如何解决这个问题?——Files Owner!

将AccessoryView.xib的Files Owner指定成该VC的类,此时再拖“连线”到VC就可以了,这样xib中按钮的事件就能直接回调到VC中我们设置的方法里了。

这是解决这个问题最简单的方法,不用写一行代码,他给我们的启示是:

xib文件是可以不依托于UIView子类、UIViewController子类单独使用的,只是这种情况比较少见,这是一个例子。

由此我们应该放开我们的思想束缚,去大胆的实践xib的各种用法。

这里再说一点:之前讲过基于UIView子类的xib的使用方法:

TestView *tView = [[NSBundle mainBundle] loadNibNamed:@"TestView" owner:self options:nil][0];

这个方法就是从bundle中找到xib文件(其实是找到被xcode编译好的nib文件)将其加载到内存中,与该xib文件是否与其他类建立了关联无关,所以这里加载xib文件也是用的此方法。

这个例子稍稍变一下需求,就是我们在一天一点xib:4简单使用xib里谈论Files Owner要说的例子了,如果点击textField的AccessoryView会有UI上的变化,或者交互的话,最好就是要创建AccessoryView.h、AccessoryView.m,然后把AccessoryView.xib分别与AccessoryView.m和VC的.m连线,UI的处理在AccessoryView.m中完成,逻辑的处理在VC的.m中完成,这样的“双连线”很好的解决了要把事件从AccessoryView.m传递给VC的.m的问题。

xib的国际化

有些app是要做国际化适配的,下面来说说xib如何做国际化,相当的简单。

1.给project添加多语言,我们以法语为例。

2.选择要国际化的xib或SB文件。

3.此时支持国际化的xib或SB文件就变成了这样:

4.下面来说说国际化中文件的组织问题:

.lproj是管理多语言相关的文件,默认工程会创建Base.lproj,里卖放xib或SB文件,我们可以在其他路径创建xib或SB文件,但是如果要使得这个文件支持国际化,系统就会把它放在Base.lproj里。我们多支持一种语言就会多创建一个.lproj文件,例如fr.lproj用于支持法语,除了Base.lproj外,其他的.lproj文件中是不放xib或SB文件的,他们会放.strings文件,.strings文件是国际化对应的重要文件,不同的语言,放在不同的.strings文件中。系统根据iOS设备当前的语言会去找到当前的.strings文件并加载里面的string,如果大家对国际化比较陌生,可以先在网上找找国际化的一些东西来看,这里默认大家对国际化是了解的。

5.开始国际化,向SB的VC中拖入一个label,显示的文字为:“你好”。拷贝object id。

6.打开xib或SB文件对应的一种语言的.strings文件(我们例子中是Main.strings(French)), 添加:

rii-xB-u6s.text = @"bonjour"

bonjour是法语中你好的意思。

7.把iOS设备的语言设置为法语,运行程序,大功告成!

两点说明

1.网上普遍流行的xib国际化的方法是用终端执行命令的方式,我个人认为,上面是最简单的方法,xib真的很强大。

2.国际化后就一定要建立一种相对应的本地化的语言,不然在切换语言的时候app还会显示之前的那种语言,这一点很重要,比如你app支持的本地语言是汉语,还想支持法语,那么一旦你国际化了,你其实要在xcode中添加两种语言,汉语和法语,生成两个.strings文件,一定切记。

User Define Runtime Attribute

之前在介绍xib右边栏的时候简单的说了一下,这是xib很高冷的一个用法,但实际上很简单,很方便,希望大家学会以后,多多使用,因为真的是太方便了。现在来具体说明。

1.拖一个view到SB的View上,为了看出效果,我这里给了它一个灰色背景。

2.设置User Define Runtime Attribute。

3.运行看效果:

是的,别怀疑,就是这么简单,把一些xib中不能设置的属性,写在这里,就可以了,弄个圆角矩形的button再也不用写代码了,方便吧。

Object

这是一个更高冷的xib用法,相对来说比较复杂,我们一点点的开始。

1.我们先建立一个Person类,继承自NSObject。

2.我们在Person类中添加一个简单的测试方法:

- (IBAction)sayHello:(id)sender {
    NSLog(@"hello person");
}

你可能会有疑问,一个继承自NSObject的类,是不能使用xib或SB的,为什么写个方法还用IBAction?而且编译器还不报错?

这里我想说:我们学习的过程其实是对现有知识、经验的不断地修正,其实科学的发展也这样。xib是可以和继承自NSObject的类发生关系的,我们往下看。

3.我们在一个VC中添加这样的代码:

#import "Person.h"

@interface ViewController : UIViewController

@property (nonatomic, strong) IBOutlet Person *aPerson;

@end

你可能同样有疑问,Person类为什么可以加IBOutlet修饰,控件栏里也没有Person类啊,为什么编译不报错?我们还是往下看。

4.我们找到ViewController对应的xib或SB文件,拖入一个button,把这个button与Person类中的sayHello:函数“连线”,你发现根本连接不上,其实也很简单,两者一点关系都没有,都不认识,怎么“连线”呢?那么我们如何让他们彼此认识?往下看。

5.在右边栏中找到Object这个对象,拖动它到左侧边栏中,把Object对象的class设置为Person,大功告成!

6.最后一步,连线,建立关系,运行测试。

这里注意,最重要的一步是要把Object对象的class设置为Person,其实xib使用中最重要地方就在class的设置,和file's Owner的设置,这是使用xib的核心。

有些人会说,这又什么卵用?

我个人认为,这种用法其实意义很大:

1.你如果遇到这种Controller需要其他类处理的情况,xib也是可以应用的,而且还是不用写什么代码,设置什么delegate,相当的方便,如果你不知道如何应用,只能说明,你之前没有想到过xib会如此的强大,可以做如此多的事情。

2.我们都熟悉Category,知道他的一个优点就是把不同的实现可以分散到不同文件中,或者把相同的功能放在一个Category中管理,更清晰,就像苹果自身的代码那样,你跳进UIViewController中看看,虽然是一个文件,但是不同功能的实现基本上都是在不同的Category中的,这给我们一个思路,如果一个VC交互复杂、UI上又很多button有很多跳转,我们完全可以用这种方式把耦合性地的部分抽离出来,或把相同功能或业务的部分抽离出来,独立封装成类,这样就可以避免几千行代码的超级VC类的出现,当大家在争着解决臃肿VC的问题而提出的各种五花八门的设计模式的时候,我想这也是一个不错的解决方案(以后会对MVC、MVP、MVVM、MVCS等各种模式提出一些自己的看法)。说到解决方案,如果有看过《Objective-C编程之道——iOS设计模式解析》这本书的朋友会知道,里面为了解决页面跳转混乱、交互复杂的情况引入了中介者模式,个人认为,充当终结者最好是一个NSObject类的子类,而xib的这种用法是中介者模式的一个很好的应用。

总结

xib给了我很大的便利,学习的过程中也给了我很多惊喜,这些高冷用法都是我在学习过程中不断总结出来的,希望大家学习完了之后和我一样,即提高了开发效率,同时学习的过程也令你愉悦。

欢迎大家和我交流沟通,若文章中有错误和纰漏,恳请指正,谢谢。

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

推荐阅读更多精彩内容