iOS实现自定义表单

作者: yanging | 来源:发表于2015-11-22 23:04 被阅读8721次

最近开发一个APP,需要让用户填写数据,然后上传到服务端进行计算并返回结果在客户端中展示。其中需要填写的数据项多达十几项,大部分是必填。所有表单数据在一个页面中实现,在APP中这样的设计其实挺逆天的,但产品经理坚持要这么弄,也只能硬着头皮写。页面的表单数据样式五花八门,下图是其中几行截图

第一、二行的 textfield 其实是一个选择框,只能从下拉选项中选择一个。第三个只允许输入数字。

页面由另一个同学实现,表单的数据基本都在 cellForRowAtIndexPath 实现,结果是这样的:

看着这么多的if...else...一下子就凌乱了。让我怎么接手实现网络接口,上传表单数据,难道也写这么多的if...else...?这么实现之后要改其中某行数据的话,比如增加或删除一行表单,就得改N个地方。这样不仅浪费时间,而且容易出错,要么改错了要么没改全。这样的代码后期维护成本太高,只能重写了。那么问题来了,怎么改?从何开始?

XLForm

XLForm is the most flexible and powerful iOS library to create dynamic table-view forms. Fully compatible with Swift & Obj-C.

XLForm 是最灵活且最强大的创建动态表单的iOS库。以下是这个库一个简单的结构图:

最主要的是红色方框的三个类:XLFormRowDescriptor, XLFormSectionDescriptor,XLFormDescriptor。XLFormDescriptor结构和UITablView一样,有Section,有Row,它就是为成为UITableView的数据源而设计的。XLFormRowDescriptor定义了每行表单的数据内容,包括行样式,标题,行类型,选择项内容,标签,合法性验证等。XLFormSectionDescriptor是由XLFormRowDescriptor组合而成的,而XLFormSectionDescriptor最终又组成了XLFormDescriptor。

由于我们要实现的APP表单行样式更复杂,有的一行要提交两项数据,所以需要对XLFormRowDescriptor做些改动。代码如下:

@property (strong, nonatomic) NSMutableDictionary *cellConfig;
@property (strong, nonatomic) NSDictionary *textFieldConfig;
@property (strong, readonly, nonatomic) NSString *rowType;
@property (strong, readonly, nonatomic) NSArray *leftOptions;
@property (strong, readonly, nonatomic) WWEFormRightSelectorOption *rightOptions;
@property (strong, nonatomic) NSString *title;
@property (strong, nonatomic) id value;
@property (strong, nonatomic) NSString *tag;
@property (nonatomic) BOOL required;

@property (strong, nonatomic) WWEBaseTableViewCell *tableViewCell;

-(id)initWithRowType:(NSString *)rowType
               title:(NSString *)title
         leftOptions:(NSArray *)leftOptions
        rightOptions:(WWEFormRightSelectorOption *)rightOptions;

-(WWEFormValidation *)doValidation;

@end

@interface WWEFormRightSelectorOption : NSObject

@property (readonly, nonatomic) NSArray *rightOptions;
@property (readonly, nonatomic) NSString *httpParameterKey;
@property (readonly, nonatomic) NSString *selectorTitle;
@property (nonatomic) NSInteger selectedIndex;

+(WWEFormRightSelectorOption *)formRightSelectorOptionWithTitle:(NSString *)title
                                               httpParameterKey:(NSString *)httpParameterKey
                                                   rightOptions:(NSArray *)rightOptions;


这样,表单数据就独立于UI,TableViewCell是可配置的。通过XLFormRowDescriptor 来配置TableViewCell。只要指定行类型,就给出相应的类型的Cell。TableViewController中的Cell是由XLFormRowDescriptor提供的。

- (WWEBaseTableViewCell *)tableViewCell {
    if (!_tableViewCell) {
        id cellClass = [self cellClassesForRowDescriptorTypes][self.rowType];
        NSAssert(cellClass, @"Not defined XLFormRowDescriptorType: %@", self.rowType ?: @"");
       
        _tableViewCell = [[cellClass alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
        
        NSAssert([_tableViewCell isKindOfClass:[WWEBaseTableViewCell class]], @"UITableViewCell must extend from WWEBaseTableViewCell");
        _tableViewCell.rowDescriptor = self;
    }
    
    return _tableViewCell;
}

那么 XLFormRowDescriptor 怎么根据不同的行给出不同的cell?cell需要继承同一个父类,这个父类足够简单,只有一个WWEFormRowDescriptor属性,其它需要实现的方法则由WWECellProtocal负责。

@class WWEFormRowDescriptor;
@interface WWEBaseTableViewCell : UITableViewCell<WWECellProtocal>

@property (nonatomic, weak) WWEFormRowDescriptor *rowDescriptor;

@end

@class WWEFormRowDescriptor;

@protocol WWECellProtocal <NSObject>

@required

@property (nonatomic, weak) WWEFormRowDescriptor *rowDescriptor;

-(void)configure;
-(void)update;


@end

另外,将表单配置数据独立出来,而不是写死在代码中。使用plist文件,这样初始化form时只需读取这个文件,就完成了cell的配置。后续如果要改动表单的内容,不改动样式,就只需修改plist文件,而无需改动代码。

最后TableViewController的代码就格外简单了:


这样上传表单数据就无比轻松了,[self.form localValidationErrors] 验证数据合法性,全部合法的话再调用 [self.form httpParameters] 就获取了所有的表单数据,传给网络接口就大功告成了。

相关文章

网友评论

  • Mr_Lucifer:请问作者 你是用的XLForm三方, 还是仿照XLForm写的框架? 因为XLForm我也用过, 一行要提交两项数据, 用自定义cell解决的, 你的解决方案没看明白 比如WWEFormRowDescriptor, WWECellProtocal 这些都是怎么创建的 如何使用, 比较好奇, 希望有个demo, 可以研究下.
  • 群星盛宴:楼主 我遇到两个问题:1,section怎么设置高度或者不显示,只显示row。2,自定义cell时,如果有表里面还有选择器类型cell,那么点击选择器会调到自定义cell里面的update方法。 请教这个怎么搞?
  • 一年影绰:求答怎么动态删除或者隐藏一行cell 我看里面那个sethidden 参数总是传不对
  • 困惑困惑困惑:如何自定义弹出视图呢
  • 勾勾手指:请问下我需要选择人员 但是这个人员列表是接口获取的,这个怎么弄啊。
  • Darren_xu:我已经手写玩了,然后看到这个文章,心塞
    Darren_xu:@Bug终结者_Seer 现在想起来满满的心塞啊
    Seer已被使用:@Darren_xu 大哥。面对20个ifesle打死我我也不会写!即时不知道xlform我也正计划自己来一个数据类型映射cell类型实现。你咋想的?
  • 游循子墨:楼主,你好。可以分享下demo么。自定义cell可以展示出来了,但是数据的获取上传没看明白。我的需求里面一个cell可能有3个数据,也可能有4个数据。
    b8a6305263f4:@我只想游来游去 @yanging 我也是遇到这个问题 ,就是 自定义了 一个cell,里面有六个数据,然后 在控制器里实例化cell,后来 获取参数时只是有这个cell的键值,获取不到那六个数据。请问是怎么解决呢?希望能给我点帮助,谢谢! 邮箱707603266@qq.com
    游循子墨:@我只想游来游去 邮箱地址是188676492@qq.com.谢谢楼主了~~~
  • Mr_Jia:楼主可以分享下自定义demo么
  • e91fb5797858:楼主 可以分享下 demo吗 :relaxed:
  • 问问问你:这里我都重写了,其实我就想知道,如果我上面这一段代码没有问题,应该是会走下去的哇。因为在创建的地方调用它就是有值的,而我在这里发现确不能改变。
  • 问问问你:还有一个问题,就是你是怎么动态刷新tablviewCell的?
    比如我在创建的时候,还没获取到数据,等到获取到了,我使用下面的方法来做属性cell或则更新cell是没有效果的。可以留下你的联系方式吗。
    // for (XLFormSectionDescriptor * section in self.form.formSections) {
    // for (XLFormRowDescriptor * row in section.formRows) {
    // if ([row.rowType isEqualToString:XLFormRowDescriptorTypeClueState])
    // {
    // [self updateFormRow:row];
    //
    // }
    //
    // }
    //
    // }
    yanging:@蒙奇D刘大侠 [self.tableView reloaddata] 就可以刷新了。重写 XLFormBaseCell update 方法,刷新的时候会调用这个方法。
  • 问问问你:这个项目有一个bug,就是当自定义和text类型都设置image的时候,他们就会存在cell.textlabel不对齐。你是怎么解决这个问题的呢?
  • 问问问你:想知道,如果设置cell.imageview这个属性,用这个应该怎么实现?
    yanging:@蒙奇D刘大侠 通过 cellConfigAtConfigure 来配置,如: XLFormRowDescriptor *row; [row.cellConfigAtConfigure setObject: @"请输入金额" forKey:@"textField.placeholder"];
  • dbfcce59c021:一行传多个值 怎么实现的 没看懂 大神能在详细说一下么
    问问问你:@好恼火 cell在创建的时候有好多方法来传值。
  • smirkk:楼主求分享 :smile:
  • 49c9990ff26c:WWEFormRowDescriptor 是怎么创建的?
  • b6e5ee0ccd39:Section 可以自定义吗
    yanging:@zxm99888 可以自定义,需要自己去实现
  • coco的日记:很实用
  • 75feab85843e:楼主 问一下 你们有没有 那种 需要填一个数字 后面选择一个单位 的 这种 需求?看了下 XLForm的demo里没有类似的~
    简单的美利坚:哈喽,那种 “需要填一个数字 后面选择一个单位 的 这种 需求”,你后面试怎么实现的
  • 75feab85843e:楼主 把你改动的 东西 放到GITHUB 瞅瞅 我觉得 我的需求和你们差不多 是CRM 里超级复杂的表单
  • 75feab85843e:不得不说 楼主 这个 很赞 我打算在我们的 CRM里用一下 如果用的好 再过来 打赏
  • 苦笑男神:要是有个XLFrom的Demo就好了!
    83096bc6ec73:@风中的温柔 不是很男,仔细研究,实在不懂也可以问我,刚刚解决项目中的所有类型的表单。
    smirkk:@yanging 看不懂demo啊
    yanging:@日月神话的爱 下载XLForm( https://github.com/xmartlabs/XLForm),这个项目本身就有Demo了
  • MR_Fany:思路很好,简介明了
  • 小小猿:虽然没有碰到,但是不得不给个赞
    yanging:@小小猿 谢谢

本文标题:iOS实现自定义表单

本文链接:https://www.haomeiwen.com/subject/oqhrhttx.html