iOS

iOS类似设置界面通用库的封装

Posted by 胡绍辉 on July 18, 2017

目录[+]

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!任然需要不停改善和优化。

知识共享许可协议
文章四川掌麦科技有限公司创作,采用知识共享署名 4.0 国际许可协议进行许可。