Swift中NSDictionaryOfVariableBindings的替代方案
有日子没写东西了,抽点时间练练笔头子,业精于勤荒于嬉~
近期从OC转到了Swift2,因为Swift一直没有正经学正经用,所以对这门语言的理解基本算是个球。。。不得不感慨苹果的动作之快。Swift还没学呢。就2了。。
。于是意识到再不学起来可能就真2了~~花了些时间看了全本的《The Swift Programming Language》总算是能用他写点东西了~跟着问题就出来了
习惯了手写自己主动布局,还是那句老话,缩写是SB(StoryBoard)的东西能好用到哪去~~可也正由于这个遇到了一些问题~在对多个视图编写VFL时的词典怎么弄?由于Swift不支持宏定义,那个让人倍感亲切的`NSDictionaryOfVariableBindings`就直接这么废掉了,网上搜了一下,也没有什么正经的解决方式~~自己手写词典。
。
。这样的土锤的编码我也实在写不出来。霎时间有种要被逼良为娼的感觉。决定转用StoryBoard来做界面。
终于结果就是。
。。我的StoryBoard用的太不熟练也好又或Xcode7的StoryBoard还有Bug也好,每次开启Xcode部分加入了约束的控件会改变大小造成设计界面与执行界面显示效果不同的警告,同一时候随着SB内容的添加电脑会变卡(尽管无损播放器的贡献更大。
。。
),至于要找特定的内容就更是一场灾难了。。。至于在IB中针对ScrollView的设计更是无力吐槽。。。毕竟缩写是SB的东西。。。整体来说对效率的没有不论什么正面影响。
。。至少对我来说~
好吧,手写自己主动布局才是真爱。
。。
于是问题又回到了原点~在Swift中该怎么处理手写自己主动布局那个麻筋儿的地方。。
。
想了想,既然不能通过宏处理,那么方法你总拦不住我吧~思路就是把view数组传过去,再通过执行时推断出对象中这几个view的变量名,手动创建一个数组模拟一下宏的功能就好了~所以写了一个针对NSObject的Extension。
为什么是NSObject?原因非常easy,大部分的手写自己主动布局是针对UIViewController编写的。但仍然会有在UIView内部写自己主动布局的情况出现~所以NSObject更加合理一些
思路有了。内容就相对简单了~
<span style="font-size:14px;"> func dictForViews(views:[UIView]) -> [String : UIView] { var count:UInt32 = 0 var dicts:[String : UIView] = [:] let ivars = class_copyIvarList(self.classForCoder, &count) for var i = 0; i < Int(count); ++i{ let obj = object_getIvar(self, ivars[i]) if let temp = obj as?UIView{ views.contains(temp) let name = String.fromCString(ivar_getName(ivars[i]))! dicts[name] = temp if dicts.count == views.count{ break } } } free(ivars) return dicts }</span>
这样生成词典的代码就能够简单的写成
<span style="font-size:14px;">let views = dictForViews([view1,view2])</span>
打印出来:
[view2: <UIView: 0x7f8820fcb610; frame = (0 0; 0 0); layer = <CALayer: 0x7f8820fc1960>>,
view1: <UIView: 0x7f8820fe17b0; frame = (0 0; 0 0); layer = <CALayer: 0x7f8820fbe560>>]
ok,基本需求搞定
顺便的,由于写了这种方法又另外写了个东西搭配使用。也是个小玩意~简单说说,正常的VFL语句写出来大体是这个样子
|-[view1(==view2)][view2]-|
我本人不喜欢写纯字符串,由于有时候为了程序的可读性,变量名字会定义的特别长~而为了写VFL要一遍遍手写这个名字实在痛苦,即便复制粘贴也是个麻烦事,所以写了一个通过对象获取属性名的方法~配合Swift的字符串插入机制用起来还是不错的。尽管VFL本身会边长,甚至些许影响了可读性,但假设足够熟悉还是没什么问题的~
方法非常easy:
<span style="font-size:14px;"> func nameFor(view:UIView) -> String{ var count:UInt32 = 0 let ivars = class_copyIvarList(self.classForCoder, &count) for var i = 0; i < Int(count); ++i{ let obj = object_getIvar(self, ivars[i]) if let temp = obj as?UIView{ if temp === view { return String.fromCString(ivar_getName(ivars[i]))! } } } free(ivars) return "" }</span>
这里返回了`String`而不是`String?
`
对于正常的Swift方法来说`String?
`显然是更合理的方式,但问题在于这种方法相当于是一个inline方法。直接用返回值就好,假设用了`String?`就须要在每次使用时加个`!`,这就太痛苦了。。
。所以我无耻的妥协了~用了这种方法之后VFL就变成了
|-[\(nameFor(view1))(==\(nameFor(view2)))][\(nameFor(view2))]-|
假设说这么写有什么优点的话,我想至少应该是有两点的,一是避免了纯手写字符串造成的输入错误,二是通过重构更改变量名的时候省去了字符串替换的麻烦~在编写的时候因为方法和变量都是智能提示的,写起来没有看上去这么麻烦~除了变长了之外其它都挺好的~到底怎么写见仁见智吧。