FXForm iOS智能表单样式制定

前言:

对于表单制作,通常我们会使用到iOS中的控件UITableView来进行制定,最终将根据用户写入的数据进行提交会展示。如果工程中不同的业务都需要用表单,那么我们可以用统一的制定样式将表单组件话,这样业务方遵守一定的规则就可以简单的制作出一份表单。 FXForm对于像设置页面或者数据输入页面是一个很好的选择,通过配置model的字段信息,就可以很快地额建立一个表单。下面就简单介绍下它的使用方法,及一些注意事项。

步骤一:创建一个form 如下:

@interface MyForm : NSObject

@property (nonatomic, copy) NSString *email;

@property (nonatomic, copy) NSString *password;

@property (nonatomic, assign) BOOL rememberMe;

@end

FXForm会根据字段类型“NSString”、“BOOL”、字段名称,“email”、“password"自动生成所需类型cell。上面的配置字段依次生成:带有UITextField类型的cell,且输入键盘为email类型,带有UITextField类型的cell,且textfield的secureTextEntry属性自动设置为yes,带有UISwitch控件的cell.对cell上的控件进行操作,会自动将改变过后的值赋值给model的字段(当向UITextField中输入文本,email、password会自动更新)

步骤二:显示一个基本表单(方式一)

展示一个表单有两种方法可以选择,一种是:FXForms提供了一个FXFormViewController,它是UIViewcontroller的子类,你可以通过FXFormViewController建立一个表单:

FXFormViewController *controller = [[FXFormViewController alloc] init]; controller.formController.form = [[MyForm alloc] init];

FXFormViewController自动包含一个UITableView。你也可以自己创建一个tableVIew,负责给它的tableView属性。大多数情况下,你需要让自己的UIViewController继承FXFormViewController,这样你就可以添加表单的创建逻辑和操作。由于FXForm管理的控制器都是以push的形式来实现转场的,所以应当将FXFormViewController(或者它的子类)放置在UIViewController中,但这并不是强制性的,这种情况是为了处理:当一个form包含subForm时,subForm会被压入导航栈,如果没有将form放置在导航中,那么subform就不会被显示。

像UITableViewController一样,FXFormViewController会将tableView设置为控制器的主视图(main view),但这不是强制的,你也可以将tableView设置成main View的subView。 和UITableViewController一样,FXFormViewController实现了UITableViewDelegate协议,如果你的视图控制器继承了FXFormViewController,你可以改写UITableViewDelegate,和UIScrollViewDelegate方法来实现自定义行为.FXFormViewController并不是tableView的直接代理,它是formController的代理,formController是FXFormController的实例。formController才是tableView的代理和数实现,然后将tableView的代理以协议的方式回调给FXFormViewController。

注意:和UITableViewController区别的是,UITableViewDataSource完全由FxFormController来实现,不建议你来改写或者截断tableView的dataSource方法是实现

步骤三:显示一个表单 (方式二)

FXFormViewController是很灵活,但是有时强制使用一个特定基类的UIViewController时,可能不是那么方便。比如:你可能希望用一个通用的UIViewController作为整个工程的视图控制器的基类,或者在一个view中显示表单。 对于前者,你可以将FXFomrViewController作为childViewController,对于后面那种情况却不适用。用FXForm但是不关联FXFormViewController,你可以直接适用FXFormController.用FXFormController来展示表单,你只需设置它的form和tableView属性就行了,余下的formController会自动处理。你也可以选择性的绑定FXFormController的delegate到当前控制器,来处理tableview的一些事件。当用自定义的表单视图控制器,一些交互性的处理,仍然适用(比如键盘的遮挡问题),但是,你需要添加视图的一些其他逻辑,比如在UIViewController的的viewWillAppear方法中,reload tableView

下面是实例代码:

@interface MyFormViewController : UIViewController

@property (nonatomic, strong) IBOutlet UITableViewtableView; @property (nonatomic, strong) FXFormControllerformController;

@end

@implementation MyFormViewController

(void)viewDidLoad { [super viewDidLoad];

//we'll assume that tableView has already been set via a nib or the -loadView method self.formController = [[FXFormController alloc] init]; self.formController.tableView = self.tableView; self.formController.delegate = self; self.formController.form = [[MyForm alloc] init]; }

(void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated];

//reload the table [self.tableView reloadData]; }

@end

*步骤四:调整表单展示 FXFomrs的最大的优点是,可以智能的猜测处理减少不必要的工作。但是有时候它不能猜测所有的工作,甚至有时候它会猜错,那我们怎么纠正它呢? 情景一:你可能不需要让所有的properies作为form field显示。你可能需要一个私有property,在form model的内部使用,或者你仅仅是为了调整form field的展示顺序。可以通过改写下列方法来实现:

(NSArray *)fields { return @[@"field1", @"field2", @"field3"]; }

返回的数组坑内包含:字符串、字典。该方法会在每次给formController的form赋值时会被调用。这就意味着你可以动态的配置表单,比如,你可以根据form的其他字段展示和隐藏一些field form。 为了调整form filed的一些展示,你可以通过两种方式进行调整:一种是在form 中添加如下方法:-(NSDictionary *)[name]Field,这里的name就是字段名。例如,如果想配置email的title:

-(NSDictionary *)emalField{ return @{FXFormFieldTitle: @"Email Address"};

} 或者你可以直接通过-fileds方法直接进行配置:

(NSArray *)fields { return @[ @{FXFormFieldKey: @"email", FXFormFieldTitle: @"Email Address"}, ...other fields... ]; }

以上两种方法本质上是一样的

情景二:如果你想配置额外的form filed(比如button、label)但是不关联form的property,你可以在-fileds 方法中进行配置。如果你想在表单末端配置一个button或者label,那么可以使用-extralFileds方法,两种方法作用一样,但是后一种“ leaves in place the default fields inferred from the form class:”

(NSArray *)extraFields { return @[ @{FXFormFieldTitle: @"Extra Field"}, ]; }

情景三:如果你不需要展示-fields中返回的form field字段,想要排除某些form field字段,那么可以使用- (NSArrayy *)excludedFields方法,将不需要展示的字段放进数组中。

(NSArray *)excludedFields { return @[ @"someProperty", @"someOtherProperty", ]; }

情景四:表单分组

对于要分组的表单,你只需要给分组的字段配置“FXFormFieldHeader”或者“FXFormFieldFooter”即可,FXFormFiedlHeader/Footer,可以是一个string,或者是一个UIView对象。

(NSArray *)fields { return @[ @{FXFormFieldKey: @"field1", FXFormFieldHeader: @"Section 1"}, @"field2", @{FXFormFieldKey: @"field3", FXFormFieldHeader: @"Section 2"}, @"field4", ]; }

总结:通过以上四个步骤就可以实现表单定制了。但这还远远不能满足我们的业务需求,比如对于没有提供的表单项样式,我们就需要自定义cell,然后注册自定义cell。那么下面我们通过简单介绍FXFomrField的properties和自定义cell的注册和使用。

static NSString *const FXFormFieldKey = @"key";

注释:form对象字段的名称,如果form对应的字段并没不是一个真正的属性(setter和getter方法的声明),这个key就是getter方法的名字,有些能够看见的field可能不会有key,比如在-fields方法中添加一个button。

static NSString *const FXFormFieldType = @"type";

注释:字段的类型,决定该字段在表单中会如何显示,一个cell,可能会对一个多种type,比如自带UITextField的cell,可能会根据不同的type来显示不同的键盘。type会根据property的声明自动匹配,当然我们也可以改写,比如NSString *email,会匹配成type为email键盘类型,我们也可以直接更改type 为label,那么cell就会显示为label类型的cell而不是自带UITextField类型的cell,线已列举的type类型可以在FXForm文件中查看,当然你也可以自定义任何一个字符串作为一个type,可以绑定自定义的cell。

static NSString *const FXFormFieldClass = @"class";

注释:字段类型,对于原始数据,它会被用来封装成OC对象(比如:数字类型的属性会转换为NSNumber类型,struct类型会转换为NSValue类型),这个转换都是自动进行的,所以很少自己进行设置,在-fields和-extraFields方法中,有时需要用它来精准指定。比如:你添加一个ViewController或者一个subform字段,有时class不能够自动指定,这是就可以通过配置class(class类或者类名)。

static NSString *const FXFormFieldCell = @"cell";

注释:用来展示field的cell,默认情况下该字段不会字段层面指定。FXFormController维护field type和cell class的映射,允许你在form层面改写默认cell和type的映射关系,而不仅仅是字段层面。如果你确实是需要指定一个一次性cell type,你可以进行改写处理。你可以给该cell提供一个class 类型对象或者代表class的name字符串

static NSString *const FXFormFieldTitle = @"title";

注释:这是字段显示的标题,FXForms会自动将驼峰样式转换为标题样式,你可以通过strings file来设置配置tile

static NSString *const FXFormFieldPlaceholder = @"placeholder";

注释:当字段的value值为空时显示的占位值,通常情况下该值为NSString类型,也可以为NSData(data 类型字段),UIImage(image 类型字段)。当用作单选或者多选是,placeHoder会以选项的第一个值作为占位值。

static NSString *const FXFormFieldDefaultValue = @"default";

注释:默认值。当字段的value为nil时,可以配置默认值。通过设置该字段,可以防止用户对value进行置空设置。默认值和占位置的区别在于,默认值是被form存储管理的,一旦设置了默认值,占位值就不会被显示。默认值只能作用于OC类型,尽管“0"是个很有意义的值(对于integer 或者float类型的字段来说),它不会被defultValue所取代。(这里的意思应该是说,OC中对于“O”的转换时无法区分该对象为nil还是该对象经转换为“0”这两种情况);

static NSString *const FXFormFieldOptions = @"options";

注释:对于任何的字段,你可以通过设置属性为NSArray类型,配置options来实现表单的选项处理,这些选项可以是NSString、NSValue等其他OC类型,你也可以提供一个FXFormFieldValueTransformer来控制实际的option value的值和需要显示在列表中的值的映射关系。相对的,你也可以通过实现- (NSString *)fieldDescription来控制自定义对象在列表中的显示。

static NSString *const FXFormFieldTemplate = @"template";

注释:该字段的了性为NSArray 或者 NSOrderSet,允许用户编辑、删除、移动编表单项、默认情况下集合类型中的表单项都是“FXFormFieldTypeText”类型的,当然你也可以改写成其他类型,template中的字段信息的配置和单个字段配置几乎一样。(实际demo中,FXFormController对表单数据源的控制是有问题的,经过移动、删除后,会经常崩溃)

static NSString *const FXFormFieldValueTransformer = @"valueTransformer";

注释:大多数情况下Form本身存储的value值和要显示给用户读取的值可能不一样,我们可以通过valueTransformer进行转换。可以通过提供block或者NSValueTransformer子类对象来处理映射,如果设置了该字段,那么-fieldDescription 方法就不会被调用了。通过设置NSValueTransformer来处理映射,该映射是可以逆向的,也就是说,可以在用户操作输入后经过转换,存储到field value中。

static NSString *const FXFormFieldAction = @"action";

注释:该字段配置是可选的。可以为方法的名字或者方法或者一个block,。当触发控件事件时就会被执行,如果action被制定为aciton,那么target可以通过响应链从cell追溯到appdelegate甚至是window中,直到找到响应。如果你的form是作为subForm,那么你也可以在parent form中对应的viewController中实现该方法。对于不可交互的字段,只有cell被选中时才会执行方法。该方法最多只能传递一个参数(通常是field cell,通过cell,你可以活着field model 和form)

static NSString *const FXFormFieldInline = @"inline";

注释:是否内联。如果字段为其他form,或者提供一个options array.通常会展示在一个viewController中,当点击cell时该viewController会被push到导航栈中。如果设置为"yes",那么这些信息都会展示在当前tableView中。

static NSString *const FXFormFieldViewController = @"controller";

注释:一些字段展示时可能是另一个view controller,当点击cell时就会push到导航栈只中,该字段制定要被push到导航栈中的viewController.

static NSString *const FXFormFieldHeader = @"header";

static NSString *const FXFormFieldFooter = @"footer";

注释:提供cell的header和footer,设置了该字段会影响dataSoure,该字段可以为字符串或者自定义view的实例

static NSString *const FXFormFieldSortable = @"sortable";

注释:字段类型为NSArray或者NSOrderSet时,设置是否可以进行排序(实际demo中进行会有bug)

字段type类型:

static NSString *const FXFormFieldTypeDefault = @"default";

默认类型,如果不指定type的话

static NSString *const FXFormFieldTypeLabel = @"label";

label类型的,自带label控件且不可交互

static NSString *const FXFormFieldTypeText = @"text";

自带UITextfield控件

static NSString *const FXFormFieldTypeLongText = @"longtext";

自带UITextView控件

static NSStringconst FXFormFieldTypeURL = @"url"; static NSStringconst FXFormFieldTypeEmail = @"email"; static NSStringconst FXFormFieldTypePassword = @"password"; static NSStringconst FXFormFieldTypeNumber = @"number"; static NSStringconst FXFormFieldTypeInteger = @"integer"; static NSStringconst FXFormFieldTypeUnsigned = @"unsigned"; static NSString *const FXFormFieldTypeFloat = @"float";

自带UITextField控件,调起键盘配置

static NSString *const FXFormFieldTypeBoolean = @"boolean";

自带UISwitch控件

static NSString *const FXFormFieldTypeOption = @"option";

右侧自带选中"check"图片

static NSStringconst FXFormFieldTypeDate = @"date"; static NSStringconst FXFormFieldTypeTime = @"time"; static NSString *const FXFormFieldTypeDateTime = @"datetime";

自带调起ImagePicker控件

static NSString *const FXFormFieldTypeImage = @"image"

自带UIImage控件

配置cell:

可以通过keyPath来进行设置,例如:

(NSDictionary *)emailField { return @{@"textLabel.color": [UIColor redColor]}; }

(NSDictionary *)nameField { return @{@"textField.autocapitalizationType": @(UITextAutocapitalizationTypeNone)}; }

自定义cell

FXForms已经提供了几种类型的cell,如果你需要添加自定义的cell来满足业务需求。或者替换掉所有的原始cell,你可以注入自定义的cell。

有两种程度的自定义:一种是直接继承FXFormCell,可以通过改写setup,update,didSelectWithTableView:controller等方法来实现自定义。setup方法会在cell创建的时候调用一次,update方法会在field value更新的时候调用。 另一种是继承自定义baseCell,那么需要采用FXFormFieldCell协议。自定义cell必须包含一个FXFormField(封装了filed的配置信息,也提供了获取和设置form value的方法,即设置field.value。)不能直接实例FXFormField,可以通过FXFormController来处理FXFormField。

可以通过以下方法自定义cell. * 可以通过FXFormFieldCell ,配置一个自定义的字段cell * 如果要关联一个type类型,可以通过formController的-registerCellClass:forFieldType:来配置 * 如果要配置指定类字段的cell,即字段是一个自定义的model类型,那么可以通过:-registerCellClass:forFieldClass:进行配置 * 如果要替换整个基类cell,那么可通过registerDefaultFieldCellClass: ,然后通过-registerCellClass:forFieldType: 进行配置。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,084评论 1 32
  • iOS开发系列--网络开发 概览 大部分应用程序都或多或少会牵扯到网络开发,例如说新浪微博、微信等,这些应用本身可...
    lichengjin阅读 3,637评论 2 7
  • 前言 说是教程,其实关键部分都是前人的工作,只是增加一点自己的经验罢了,顺便当作一个记录。主要这个电脑本身就挺适合...
    与之书阅读 49,692评论 24 18