目录[+]
Top
想法和由来
基本每个App都有设置界面,设置一些常见的功能,头像、昵称、个人签名等等。常规的设置界面比如微信设置界面,个人资料界面,复杂的设置界面比如含有IM的群设置界面,知乎App的设置界面。当设置界面设置的功能太多,或者设置界面在App中有太多的地方都需要使用时,对我们开发者来说,就是一件痛苦的事,只是换了数据源而已。通用做法写一个tableView各种判断。其实仔细一想这种类似界面其实跟IM聊天的会话界面类似,消息会话界面一般包含文本消息、图片消息、语言消息、自定义消息。鉴于之前IM的UI逻辑,于是封装的想法油然而生,封装后大家和自己用起来都很方便了。
技术选择
-
a.设计模式 -> MVC
-
b.界面布局 -> 代码AutoLayout,包括适配ipad
-
c.图片下载 -> 依赖SDWebImage
- 关于设计模式,经典的设计模式比如MVC。其它的设计模式MVVM、MVP、MVCS可以阅读这篇文章 因为涉及到的内容没有IM复杂,只是对界面进行简单的逻辑封装和UI处理,所以这里并没有使用其它设计模式所以采用了MVC。对于一个复杂的即时通讯UI的话,应该是多种模式的结合处理会更好,这里推荐一个网易的开源UI组件参考学习
-
关于布局,本库关于界面的控件并不是太多。参考环信库,UI也是用NSLayoutConstraint布局。减少依赖,自定义cell你可以使用自带布局或者第三方库布局,由于本库采用了很多继承关系,暂不支持xib/storyBoard。需要代码实现界面布局。
- 关于图片下载,做好一个完整的图片下载并不是一件容易的事,既然有成熟第三方库SDWebImage,何不站在巨人肩膀上依赖下呢?
架构图
内容分析
由于只是对UI界面的简单封装,本库并不涉及太多很深奥的知识,仅仅只是在细节上有很多处理。下面就几个方面介绍本库的一些小优势。
-
配置属性完善。满足基本上所有app的类似界面,箭头,分割线,字体大小,颜色,文本位置等等都可以配置。这些属性可以在HSBaseCellModel、HSTitleCellModel、HSImageCellModel、HSSwitchCellModel等类里面找到。
-
耦合性特别低。每一种模型对应一种cell,逻辑处理层次明确,从扩展到定位bug都是特别容易,由于有继承关系,每个类处理自己的业务,如果需要自己处理,则需要拦截其业务。
-
断言处理。如果由于使用者外部没有按照库的某些规则组装和使用,用断言提醒使用者就比较友好,相关一些代码如下演示:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { NSMutableArray *sections = [self.hs_dataArry hs_objectWithIndex:indexPath.section]; NSAssert([sections isKindOfClass:[NSMutableArray class]], @"此对象必须为一个可变数组,请检查数据源组装方式是否正确!"); HSBaseCellModel *cellModel = (HSBaseCellModel *)[sections hs_objectWithIndex:indexPath.row]; Class class = NSClassFromString(cellModel.cellClass); return [class getCellHeight:cellModel]; }
以上所示,如果外部传的数组不是可变数组,而是不可变数组,这样就会崩溃该断言!
-
个别属性内部保护。如果由于外部胡乱更改一些内部十分重要的属性导致崩溃,那这个库是不完美的。而HSBaseCellModel中的identifier和cellClass分别是模型唯一标识符和寻找cell类名的重要属性,一旦初始化,就不可更改。即使尝试用kvc修改,也应该崩溃。所以它的定义应该是这样:
@property (nonatomic, copy,readonly)NSString *identifier; ///<唯一标识符(更新会用到)
@property (nonatomic, copy,readonly)NSString *cellClass; ///<该模型绑定的cell类名
- 唯一标识符的设定。每一个模型初始化完成后都应该有一个自己的唯一标识符,因为动态更新视图内部原理就是通过唯一标识符找到模型然后替换,再刷新表格。所以这个唯一标识符是必须唯一,那么怎么才能让这个唯一标识符唯一呢?我最开始是用获取当前时间:
CFAbsoluteTime now = CFAbsoluteTimeGetCurrent()
这样看起来没什么大问题,每个模型都是按照顺序初始化,大多数情况下不会出问题。因为模型就是界面的数据源,这种数据源一般不得超过20条,但是理论上是存在bug的,下面情况我自己测试过,如果我同时并发线程,创建大量的模型,不同线程有时会同一时间创建,即使这个时间已经十分十分细了。但是同一线程肯定肯定有时间差,由此我的唯一标示符由:当前时间+当前线程ID组成。相关代码如下:
- (instancetype)init
{
if(self = [super init]){
//获取当前时间
CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
//获取当前线程id
NSString *threadNumber = [[[NSString stringWithFormat:@"%@",[NSThread currentThread]] componentsSeparatedByString:@"number = "].lastObject componentsSeparatedByString:@","].firstObject;
privateCellModelIdentifier = [NSString stringWithFormat:@"%lf%@",now,threadNumber];
}
return self;
}
相关链接
Github源码地址 做一个东西不难,做好一个东西还是很难,即使看起来很简单的。81个commits和16个release!任然需要不停改善和优化。
