您的位置:首页 > 移动开发 > Swift

Swift中NSDictionaryOfVariableBindings的替代方案

2015-07-16 15:34 351 查看
有日子没写东西了,抽点时间练练笔头子,业精于勤荒于嬉~

最近从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?原因很简单,大部分的手写自动布局是针对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本身会边长,甚至些许影响了可读性,但如果足够熟悉还是没什么问题的~

方法很简单:

<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))]-|

如果说这么写有什么好处的话,我想至少应该是有两点的,一是避免了纯手写字符串造成的输入错误,二是通过重构更改变量名的时候省去了字符串替换的麻烦~在编写的时候由于方法和变量都是智能提示的,写起来没有看上去这么麻烦~除了变长了之外其他都挺好的~究竟怎么写见仁见智吧。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息