作者:XMARTLABS。
如果您正在寻找 Swift 原生实现,我们最近创建了一个名为 Eureka 的项目,它是 XLForm 在 Swift 中的完全重新设计。不要担心,我们还将继续维护和改进 XLForm,obj-c 是无敌的!!
XLForm 是创建动态表格视图表单的最灵活和强大的 iOS 库。库的目标是获得手写表单相同的强大功能,但只需花十分之一的时间。
XLForm 提供了一个非常强大的 DSL(特定领域语言),用于创建表单。它在运行时跟踪此规范,并动态更新 UI。
#####以下是一个使用 XLForm 创建的 iOS 日历事件表单示例
NSPredicates
声明性完成。(请参阅 根据其他行的值隐藏或显示行或部分)class CalendarEventFormViewController : XLFormViewController {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.initializeForm()
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
self.initializeForm()
}
func initializeForm() {
// Implementation details covered in the next section.
}
}
#import "XLFormViewController.h"
@interface CalendarEventFormViewController: XLFormViewController
@end
@interface ExamplesFormViewController ()
@end
@implementation ExamplesFormViewController
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self){
[self initializeForm];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self){
[self initializeForm];
}
return self;
}
- (void)initializeForm {
// Implementation details covered in the next section.
}
@end
要创建一个表单,我们应该通过 XLFormDescriptor
实例声明它并将其分配给一个 XLFormViewController
实例。正如我们所说的,XLForm 工作基于 DSL,它隐藏了复杂和样板代码,而没有失去手写表单的强大和灵活性。
为了定义一个表单,我们使用 3 个类
XLFormDescriptor
XLFormSectionDescriptor
XLFormRowDescriptor
表单定义是一个包含一个或多个部分(XLFormSectionDescriptor
实例)的 XLFormDescriptor
实例,每个部分包含多个行(《XLFormRowDescriptor 实例)。如您所注意到的,DSL 结构类似于 UITableView
(表格 -->> 部分 --> 行)。生成的表格视图表单的结构(部分和行顺序)与定义的结构相对应。
- (void)initializeForm {
XLFormDescriptor * form;
XLFormSectionDescriptor * section;
XLFormRowDescriptor * row;
form = [XLFormDescriptor formDescriptorWithTitle:@"Add Event"];
// First section
section = [XLFormSectionDescriptor formSection];
[form addFormSection:section];
// Title
row = [XLFormRowDescriptor formRowDescriptorWithTag:@"title" rowType:XLFormRowDescriptorTypeText];
[row.cellConfigAtConfigure setObject:@"Title" forKey:@"textField.placeholder"];
[section addFormRow:row];
// Location
row = [XLFormRowDescriptor formRowDescriptorWithTag:@"location" rowType:XLFormRowDescriptorTypeText];
[row.cellConfigAtConfigure setObject:@"Location" forKey:@"textField.placeholder"];
[section addFormRow:row];
// Second Section
section = [XLFormSectionDescriptor formSection];
[form addFormSection:section];
// All-day
row = [XLFormRowDescriptor formRowDescriptorWithTag:@"all-day" rowType:XLFormRowDescriptorTypeBooleanSwitch title:@"All-day"];
[section addFormRow:row];
// Starts
row = [XLFormRowDescriptor formRowDescriptorWithTag:@"starts" rowType:XLFormRowDescriptorTypeDateTimeInline title:@"Starts"];
row.value = [NSDate dateWithTimeIntervalSinceNow:60*60*24];
[section addFormRow:row];
self.form = form;
}
XLForm将从先前解释的定义中加载表格视图表单。最有意思的部分是,它将根据表单定义的修改更新表格视图表单。
这意味着我们可以在运行时对表格视图表单做出更改,添加或删除部分定义或行定义到表单定义中,您再也不需要关心 NSIndexPath
、UITableViewDelegate
UITableViewDataSource 或其他复杂性。
要查看更复杂的形式定义,请查看此存储库 Examples 目录中的示例应用程序。 如果您愿意,您还可以在自己的设备上运行示例。XLForm不依赖于其他 pods,但示例项目使用一些 Cocoapods 来展示 XLForm 的先进功能。
[email protected]:xmartlabs/XLForm.git
。您可以选择将存储库分叉并从您自己的 GitHub 账户克隆它,如果您想要贡献,这种方法会更好。pod install
。####输入行
输入行允许用户输入文本文值。基本上,它们使用 UITextField
或 UITextView
控件。输入行类型之间的主要区别在于 keyboardType
、autocorrectionType
和 autocapitalizationType
配置。
static NSString *const XLFormRowDescriptorTypeText = @"text";
将表示为一个具有 UITextAutocorrectionTypeDefault
、UITextAutocapitalizationTypeSentences
和 UIKeyboardTypeDefault
的 UITextField
。
static NSString *const XLFormRowDescriptorTypeName = @"name";
将表示为一个具有 UITextAutocorrectionTypeNo
、UITextAutocapitalizationTypeWords
和 UIKeyboardTypeDefault
的 UITextField
。
static NSString *const XLFormRowDescriptorTypeURL = @"url";
将表示为一个具有 UITextAutocorrectionTypeNo
、UITextAutocapitalizationTypeNone
和 UIKeyboardTypeURL
的 UITextField
。
static NSString *const XLFormRowDescriptorTypeEmail = @"email";
将表示为一个具有 UITextAutocorrectionTypeNo
、UITextAutocapitalizationTypeNone
和 UIKeyboardTypeEmailAddress
的 UITextField
。
static NSString *const XLFormRowDescriptorTypePassword = @"password";
将表示为一个具有 UITextAutocorrectionTypeNo
、UITextAutocapitalizationTypeNone
和 UIKeyboardTypeASCIICapable
的 UITextField
。
此行类型还设置 secureTextEntry
为 YES
以隐藏用户输入的内容。
static NSString *const XLFormRowDescriptorTypeNumber = @"number";
将表示为一个具有 UITextAutocorrectionTypeNo
、UITextAutocapitalizationTypeNone
和 UIKeyboardTypeNumbersAndPunctuation
的 UITextField
。
static NSString *const XLFormRowDescriptorTypePhone = @"phone";
将表示为一个具有 UIKeyboardTypePhonePad
的 UITextField
。
static NSString *const XLFormRowDescriptorTypeTwitter = @"twitter";
将表示为一个具有 UITextAutocorrectionTypeNo
、UITextAutocapitalizationTypeNone
和 UIKeyboardTypeTwitter
的 UITextField
。
static NSString *const XLFormRowDescriptorTypeAccount = @"account";
将表示为一个具有 UITextAutocorrectionTypeNo
、UITextAutocapitalizationTypeNone
和 UIKeyboardTypeDefault
的 UITextField
。
static NSString *const XLFormRowDescriptorTypeInteger = @"integer";
将由具有 UIKeyboardTypeNumberPad
的 UITextField
表示。
static NSString *const XLFormRowDescriptorTypeDecimal = @"decimal";
将由具有 UIKeyboardTypeDecimalPad
的 UITextField
表示。
static NSString *const XLFormRowDescriptorTypeTextView = @"textView";
将由具有 UITextView
和 UITextAutocorrectionTypeDefault
、UITextAutocapitalizationTypeSentences
以及 UIKeyboardTypeDefault
的 UITextView
表示。
####选择器行
选择器行允许我们从列表中选择值或多个值。XLForm支持开箱即用的8种选择器类型。
static NSString *const XLFormRowDescriptorTypeSelectorPush = @"selectorPush";
static NSString *const XLFormRowDescriptorTypeSelectorActionSheet = @"selectorActionSheet";
static NSString *const XLFormRowDescriptorTypeSelectorAlertView = @"selectorAlertView";
static NSString *const XLFormRowDescriptorTypeSelectorLeftRight = @"selectorLeftRight";
static NSString *const XLFormRowDescriptorTypeSelectorPickerView = @"selectorPickerView";
static NSString *const XLFormRowDescriptorTypeSelectorPickerViewInline = @"selectorPickerViewInline";
static NSString *const XLFormRowDescriptorTypeSelectorSegmentedControl = @"selectorSegmentedControl";
static NSString *const XLFormRowDescriptorTypeMultipleSelector = @"multipleSelector";
通常我们会有一组对象用于选择(这些对象应该有一个用于显示的字符串和一个用于序列化的值),XLForm必须能够显示这些对象。
XLForm显示对象的规则如下:
XLFormRowDescriptor
对象的值为nil,XLForm将使用noValueDisplayText
属性作为显示文本。valueTransformer
属性值。XLForm使用NSValueTransformer
将所选对象转换为NSString。NSString
或NSNumber
,它将使用对象的description
属性。XLFormOptionObject
,XLForm将从formDisplayText
方法获取显示值。:)
生效。您可能需要在设置noValueDisplayText
或valueTransformer
属性或将选择器选项对象符合XLFormOptionObject
协议的情况下更改显示文本。
这是协议声明
@protocol XLFormOptionObject <NSObject>
@required
-(NSString *)formDisplayText;
-(id)formValue;
@end
####日期和时间行
XLForms支持3种日期类型:Date
、DateTime
、Time
和Countdown Timer
,并且能够以两种不同的方式展示UIDatePicker
控件,即内联和非内联。
static NSString *const XLFormRowDescriptorTypeDateInline = @"dateInline";
static NSString *const XLFormRowDescriptorTypeDateTimeInline = @"datetimeInline";
static NSString *const XLFormRowDescriptorTypeTimeInline = @"timeInline";
static NSString *const XLFormRowDescriptorTypeCountDownTimerInline = @"countDownTimerInline";
static NSString *const XLFormRowDescriptorTypeDate = @"date";
static NSString *const XLFormRowDescriptorTypeDateTime = @"datetime";
static NSString *const XLFormRowDescriptorTypeTime = @"time";
static NSString *const XLFormRowDescriptorTypeCountDownTimer = @"countDownTimer";
以下是如何定义这些行类型的一个示例
Objective C
XLFormDescriptor * form;
XLFormSectionDescriptor * section;
XLFormRowDescriptor * row;
form = [XLFormDescriptor formDescriptorWithTitle:@"Dates"];
section = [XLFormSectionDescriptor formSectionWithTitle:@"Inline Dates"];
[form addFormSection:section];
// Date
row = [XLFormRowDescriptor formRowDescriptorWithTag:kDateInline rowType:XLFormRowDescriptorTypeDateInline title:@"Date"];
row.value = [NSDate new];
[section addFormRow:row];
// Time
row = [XLFormRowDescriptor formRowDescriptorWithTag:kTimeInline rowType:XLFormRowDescriptorTypeTimeInline title:@"Time"];
row.value = [NSDate new];
[section addFormRow:row];
// DateTime
row = [XLFormRowDescriptor formRowDescriptorWithTag:kDateTimeInline rowType:XLFormRowDescriptorTypeDateTimeInline title:@"Date Time"];
row.value = [NSDate new];
[section addFormRow:row];
// CountDownTimer
row = [XLFormRowDescriptor formRowDescriptorWithTag:kCountDownTimerInline rowType:XLFormRowDescriptorTypeCountDownTimerInline title:@"Countdown Timer"];
row.value = [NSDate new];
[section addFormRow:row];
Swift
static let dateTime = "dateTime"
static let date = "date"
static let time = "time"
var form : XLFormDescriptor
var section : XLFormSectionDescriptor
var row : XLFormRowDescriptor
form = XLFormDescriptor(title: "Dates") as XLFormDescriptor
section = XLFormSectionDescriptor.formSectionWithTitle("Inline Dates") as XLFormSectionDescriptor
form.addFormSection(section)
// Date
row = XLFormRowDescriptor(tag: tag.date, rowType: XLFormRowDescriptorTypeDateInline, title:"Date")
row.value = NSDate()
section.addFormRow(row)
// Time
row = XLFormRowDescriptor(tag: tag.time, rowType: XLFormRowDescriptorTypeTimeInline, title: "Time")
row.value = NSDate()
section.addFormRow(row)
// DateTime
row = XLFormRowDescriptor(tag: tag.dateTime, rowType: XLFormRowDescriptorTypeDateTimeInline, title: "Date Time")
row.value = NSDate()
section.addFormRow(row)
self.form = form;
####布尔行
XLForms支持两种布尔控件
static NSString *const XLFormRowDescriptorTypeBooleanCheck = @"booleanCheck";
static NSString *const XLFormRowDescriptorTypeBooleanSwitch = @"booleanSwitch";
我们还可以使用选择器行部分中介绍的选择器行类型来模拟其他类型的布尔行。
####其他行
#####步进器
XLForms支持使用UIStepper控件进行计数。
static NSString *const XLFormRowDescriptorTypeStepCounter = @"stepCounter";
您可以轻松设置步进器参数
row = [XLFormRowDescriptor formRowDescriptorWithTag:kStepCounter rowType:XLFormRowDescriptorTypeStepCounter title:@"Step counter"];
row.value = @50;
[row.cellConfigAtConfigure setObject:@YES forKey:@"stepControl.wraps"];
[row.cellConfigAtConfigure setObject:@10 forKey:@"stepControl.stepValue"];
[row.cellConfigAtConfigure setObject:@10 forKey:@"stepControl.minimumValue"];
[row.cellConfigAtConfigure setObject:@100 forKey:@"stepControl.maximumValue"];
#####滑块
XLForms支持使用UISlider控件进行计数。
static NSString *const XLFormRowDescriptorTypeSlider = @"slider";
您可以非常容易地调整滑块以满足您的需求
row = [XLFormRowDescriptor formRowDescriptorWithTag:kSlider rowType:XLFormRowDescriptorTypeSlider title:@"Slider"];
row.value = @(30);
[row.cellConfigAtConfigure setObject:@(100) forKey:@"slider.maximumValue"];
[row.cellConfigAtConfigure setObject:@(10) forKey:@"slider.minimumValue"];
[row.cellConfigAtConfigure setObject:@(4) forKey:@"steps"];
将steps
设置为@(0)
以禁用步进功能。
#####信息
有时我们的应用程序需要显示不可编辑的数据。XLForm为我们提供了XLFormRowDescriptorTypeInfo
行类型来显示不可编辑的信息。使用示例可以是显示应用程序版本在应用程序设置部分。
#####按钮
除了数据输入行、不可编辑的行和选择器之外,XLForm还有一个按钮行XLFormRowDescriptorTypeButton
,它允许在选中时执行任何操作。它可以通过块(闭包)、选择器、撤销标识符、撤销类或指定要呈现的视图控制器进行配置。可以基于视图控制器类、视图控制器的故事板ID或nib名称设置视图控制器 specification。nib名称必须与视图控制器类名称匹配。
任何XLFormSectionDescriptor
对象都可以设置为支持行插入、删除或重新排序。可能只启用这些方式中的一种,也可能启用组合或全部。
多值部分只是一个支持这些模式之一的部分。
多值XLFormSectionDescriptor
最有趣的部分是它支持行部分中显示的所有类型以及自定义行。
创建多值部分非常简单,只需使用以下便利XLFormSectionDescriptor
初始化器之一即可
+(id)formSectionWithTitle:(NSString *)title
sectionOptions:(XLFormSectionOptions)sectionOptions;
+(id)formSectionWithTitle:(NSString *)title
sectionOptions:(XLFormSectionOptions)sectionOptions
sectionInsertMode:(XLFormSectionInsertMode)sectionInsertMode;
sectionOptions
是一个位枚举参数,用于选择多值部分类型(插入、删除、重新排序)。可用选项包括 XLFormSectionOptionCanInsert
、XLFormSectionOptionCanDelete
、XLFormSectionOptionCanReorder
。默认使用的是 XLFormSectionOptionNone
。
sectionInsertMode
可以用来选择插入模式的外观。XLform 自带两种插入模式:XLFormSectionInsertModeLastRow
和 XLFormSectionInsertModeButton
。XLFormSectionInsertModeLastRow
是默认值。
让我们来看看如何创建一个多值部分
XLFormDescriptor * form;
XLFormSectionDescriptor * section;
XLFormRowDescriptor * row;
NSArray * nameList = @[@"family", @"male", @"female", @"client"];
form = [XLFormDescriptor formDescriptorWithTitle:@"Multivalued examples"];
// Enable Insertion, Deletion, Reordering
section = [XLFormSectionDescriptor formSectionWithTitle:@"MultiValued TextField"
sectionOptions:XLFormSectionOptionCanReorder | XLFormSectionOptionCanInsert | XLFormSectionOptionCanDelete];
section.multivaluedTag = @"textFieldRow";
[form addFormSection:section];
for (NSString * tag in nameList) {
// add a row to the section, each row will represent a name of the name list array.
row = [XLFormRowDescriptor formRowDescriptorWithTag:nil rowType:XLFormRowDescriptorTypeText title:nil];
[[row cellConfig] setObject:@"Add a new tag" forKey:@"textField.placeholder"];
row.value = [tag copy];
[section addFormRow:row];
}
// add an empty row to the section.
row = [XLFormRowDescriptor formRowDescriptorWithTag:nil rowType:XLFormRowDescriptorTypeText title:nil];
[[row cellConfig] setObject:@"Add a new tag" forKey:@"textField.placeholder"];
[section addFormRow:row];
您可以通过调用 -(NSDictionary *)formValues;
来获取所有表单值,既可以是 XLFormViewController
实例或 XLFormDescriptor
实例。
返回的 NSDictionary
是根据以下规则创建的:
XLForm
为属于没有设置 multivaluedTag
值的 XLFormSectionDescriptor
的每个 XLFormRowDescriptor
添加一个值。字典键是 XLFormRowDescriptor
的 tag
属性值。
对于具有 multivaluedTag
值的每个部分,XLForm 都会添加一个以 NSArray
作为值的字典项,数组中的每个值都是部分中每行的值,键是 multivaluedTag
。
例如,如果我们有一个具有等于 tags
的 multivaluedTag
属性和以下值的部分行的集合:'family"、'male"、'female"、'client',生成的值将是 tags: ['family', 'male', 'female', 'client']
在某些情况下,我们需要的表单值可能与 XLFormRowDescriptor
实例的值不同。这种情况通常出现在选择行上,以及当我们需要将表单值发送到某个端点时,所选值可能是一个核心数据对象或其他对象。在这种情况下,XLForm 需要知道如何获取所选对象的值和描述。
当使用 -(NSDictionary *)httpParameters
方法时,XLForm 遵循以下规则来获取 XLFormRowDescriptor
的值:
NSString
、NSNumber
或 NSDate
,则值是该对象本身。XLFormOptionObject
协议,XLForm 从 formValue
方法中获取值。multivaluedTag
与 formValues
方法中的方式一样工作。
要创建自定义单元格,您需要创建一个从 XLFormBaseCell
扩展的 UITableViewCell
。XLFormBaseCell
符合 XLFormDescriptorCell
协议。
您可能希望实现 XLFormDescriptorCell
方法来更改单元格行为。
@protocol XLFormDescriptorCell <NSObject>
@required
@property (nonatomic, weak) XLFormRowDescriptor * rowDescriptor;
// initialise all objects such as Arrays, UIControls etc...
-(void)configure;
// update cell when it about to be presented
-(void)update;
@optional
// height of the cell
+(CGFloat)formDescriptorCellHeightForRowDescriptor:(XLFormRowDescriptor *)rowDescriptor;
// called to check if cell can became first responder
-(BOOL)formDescriptorCellCanBecomeFirstResponder;
// called to ask cell to assign first responder to relevant UIView.
-(BOOL)formDescriptorCellBecomeFirstResponder;
// called when cell is selected
-(void)formDescriptorCellDidSelectedWithFormController:(XLFormViewController *)controller;
// http parameter name used for network request
-(NSString *)formDescriptorHttpParameterName;
// is invoked when cell becomes firstResponder, could be used for change how the cell looks like when it's the forst responder.
-(void)highlight;
// is invoked when cell resign firstResponder
-(void)unhighlight;
@end
一旦创建了自定义单元格,您需要通过将行定义添加到 cellClassesForRowDescriptorTypes
字典中来让 XLForm
了解此单元格。
[[XLFormViewController cellClassesForRowDescriptorTypes] setObject:[MYCustomCellClass class] forKey:kMyAppCustomCellType];
或,如果我们使用 nib 文件来定义了 XLBaseDescriptorCell
[[XLFormViewController cellClassesForRowDescriptorTypes] setObject:@"nibNameWithoutNibExtension" forKey:kMyAppCustomCellType];
这样做后,XLForm 将在用到 kMyAppCustomCellType 行类型时实例化适当的单元格类。
基本选择器通常就足够了,它允许用户从推送的视图控制器中选择一项或多项,但有时我们需要更多灵活性,为用户提供更好的用户体验或执行一些默认情况下不支持的操作。
假设您的应用用户需要选择地图坐标或从服务器端点选择值,我们如何轻松地做到这一点?
像这样定义之前的选择器行非常简单...
row = [XLFormRowDescriptor formRowDescriptorWithTag:kSelectorMap rowType:XLFormRowDescriptorTypeSelectorPush title:@"Coordinate"];
// set up the selector controller class
row.action.viewControllerClass = [MapViewController class];
// or
//row.action.viewControllerStoryboardId = @"MapViewControllerStoryboardId";
// or
//row.action.viewControllerNibName = @"MapViewControllerNibName";
// Set up a NSValueTransformer to convert CLLocation to NSString, it's used to show the select value description (text).
row.valueTransformer = [CLLocationValueTrasformer class];
// Set up the default value
row.value = [[CLLocation alloc] initWithLatitude:-33 longitude:-56];
action.viewControllerClass
控制器类应符合 XLFormRowDescriptorViewController
协议。
在上面的例子中,MapViewController
符合 XLFormRowDescriptorViewController
。
@protocol XLFormRowDescriptorViewController <NSObject>
@required
@property (nonatomic) XLFormRowDescriptor * rowDescriptor;
@end
XLForm 使用属于选择器行的 XLFormRowDescriptor
实例设置 rowDescriptor
属性。
开发者负责使用 rowDescriptor
值更新其视图,并在自定义选择器视图控制器内部将选中值设置为 rowDescriptor
。
注意:属性
viewControllerClass
、viewControllerNibName
或viewControllerStoryboardId
是互斥的,并由XLFormButtonCell
和XLFormSelectorCell
使用。如果您创建了自定义单元格,那么您负责使用它们。
row = [XLFormRowDescriptor formRowDescriptorWithTag:kSelectorUser rowType:XLFormRowDescriptorTypeSelectorPush title:@"User"];
row.action.viewControllerClass = [UsersTableViewController class];
您可以在示例仓库文件夹中找到这些示例的详细内容,例如,在 Examples/Objective-C/Examples/Selectors/CustomSelectors 和 Examples/Objective-C/Examples/Selectors/DynamicSelector。
对 XLFormDescriptor
所做的任何更改都将反映在 XLFormViewController
的 tableView 上。这意味着当添加或删除整个节或行时,XLForm 会相应地动画化整个节或行。
我们不需要处理 NSIndexPath
或添加、删除 UITableViewCell
。随着时间推移,特定 UITableViewCell
的 NSIndexPath
会发生变化,这使得难以追踪每个 UITableViewCell
的 NSIndexPath
。
每个 XLForm XLFormRowDescriptor
行都有一个在构造函数中设置的 tag
属性。除了其他辅助工具外,XLFormDescriptor
还有一个特定于获取带有 tag
的 XLFormRowDescriptor
的方法。
使用标签管理 XLFormRowDescriptor
更容易,标签应该是唯一的,并且在表视图添加、修改或删除时不会改变。
重要的是要注意,所有 UITableView
表单修改都必须使用描述符进行,而不是直接在 UITableView
上进行修改。
通常,您可能希望在某个值更改或添加或删除行或节时更改表单。为此,您可以为行或节设置 disabled
和 hidden
属性。更多详细信息,请参阅 根据其他行的值使行或节不可见。
为了与表单描述符的修改保持同步,您的 XLFormViewController
子类应该重写 'XLFormViewController' 的 XLFormDescriptorDelegate
方法。
注意:重写此代理方法时,始终调用
[super ...]
方法。
@protocol XLFormDescriptorDelegate <NSObject>
@required
-(void)formSectionHasBeenRemoved:(XLFormSectionDescriptor *)formSection atIndex:(NSUInteger)index;
-(void)formSectionHasBeenAdded:(XLFormSectionDescriptor *)formSection atIndex:(NSUInteger)index;
-(void)formRowHasBeenAdded:(XLFormRowDescriptor *)formRow atIndexPath:(NSIndexPath *)indexPath;
-(void)formRowHasBeenRemoved:(XLFormRowDescriptor *)formRow atIndexPath:(NSIndexPath *)indexPath;
-(void)formRowDescriptorValueHasChanged:(XLFormRowDescriptor *)formRow oldValue:(id)oldValue newValue:(id)newValue;
-(void)formRowDescriptorPredicateHasChanged:(XLFormRowDescriptor *)formRow
oldValue:(id)oldValue
newValue:(id)newValue
predicateType:(XLPredicateType)predicateType;
@end
例如,如果我们想根据另一行值显示或隐藏一行
-(void)formRowDescriptorValueHasChanged:(XLFormRowDescriptor *)rowDescriptor oldValue:(id)oldValue newValue:(id)newValue
{
// super implmentation MUST be called
[super formRowDescriptorValueHasChanged:rowDescriptor oldValue:oldValue newValue:newValue];
if ([rowDescriptor.tag isEqualToString:@"alert"]){
if ([[rowDescriptor.value valueData] isEqualToNumber:@(0)] == NO && [[oldValue valueData] isEqualToNumber:@(0)]){
XLFormRowDescriptor * newRow = [rowDescriptor copy];
[newRow setTag:@"secondAlert"];
newRow.title = @"Second Alert";
[self.form addFormRow:newRow afterRow:rowDescriptor];
}
else if ([[oldValue valueData] isEqualToNumber:@(0)] == NO && [[newValue valueData] isEqualToNumber:@(0)]){
[self.form removeFormRowWithTag:@"secondAlert"];
}
}
###概要
XLForm 允许您定义行之间的依赖关系,以便如果一行值更改,另一行的行为将自动更改。例如,您可能有一个问题用户是否拥有宠物的表单。如果答案是 '是',您可能想询问它们的名字。
因此,您可以根据其他行的值使行不可见,并再次可见。同样,这也适用于节。
请看以下示例
当然,您也可以通过手动观察某些行的值并根据需要删除和添加行来手动执行此操作,但这将会是一大堆工作,而这些工作已经完成了。
###工作原理
为了使行和节的显示和隐藏自动化,每个描述符中都有一个属性
@property id hidden;
此 id 对象通常是一个 NSPredicate 或包含 BOOL 的 NSNumber。可以使用任意一个或最终使用可以从中创建 NSPredicate 的 NSString 设置它。为了使此功能正常工作,字符串必须具有正确的语法。
例如,您可以将以下字符串设置为一行(second
),使其在包含“hide”值的上一行(first
)消失。
second.hidden = [NSString stringWithFormat:@"$%@ contains[c] 'hide'", first];
这将把first
标签放在'$'之后,当然您也可以手动操作。当谓词被评估时,每个标签变量都会被相应的行描述符替换。
当参数是一个NSString时,除非标签后面跟着'.isHidden'或'.isDisabled',否则会为每个标签添加'.value'。这意味着一个行(或部分)可能依赖于其他行的value
、hidden
或disabled
属性。如果直接使用NSPredicate设置属性,它的格式字符串将不会被修改(因此,如果你想引用其值,必须在每个变量后附加一个'.value')。设置NSString是最简单的方法,但对于一些复杂的谓词可能无法正常工作,因此对于这些谓词,您应该直接设置NSPredicate。
您也可以使用bool对象来设置这些属性,这意味着除非手动设置,否则属性值不会改变。
要获取评估后的布尔值,应调用isHidden
方法。它不会每次被调用时都重新评估谓词,而只是在它所依赖的行的值(或隐藏/禁用状态)改变时才进行评估。当发生这种情况并且返回值改变时,它会自动在表单上反映这一变化,从而不需要调用其他方法。
下面是一个更复杂的例子:
可以禁用行,这样用户就不能更改它们。默认情况下,禁用行的文字颜色为灰色。要禁用一行,要做的只是设置它的禁用属性
@property id disabled;
该属性预期包含一个BOOL的NSNumber或一个NSString或一个NSPredicate。布尔值将静态地禁用(或启用)行。其他两个与上文中解释的隐藏属性的工作方式相同。这意味着一个行可以根据其他行的值被禁用或启用。当设置NSString时,将使用字符串作为格式字符串生成NSPredicate,因此它必须为了这个目的保持一致。
与隐藏属性的区别是,检查行的禁用状态不会自动将此值反映在表单上。因此,应调用XLFormViewController的updateFormRow方法。
我们可以使用XLForm验证支持来验证表单数据。
XLFormRowDescriptor
实例包含一个验证器列表。我们可以使用这些方法添加验证器、删除验证器以及对特定行进行验证
-(void)addValidator:(id<XLFormValidatorProtocol>)validator;
-(void)removeValidator:(id<XLFormValidatorProtocol>)validator;
-(XLFormValidationStatus *)doValidation;
我们可以定义自己的自定义验证器,只需定义一个符合XLFormValidatorProtocol
的对象。
@protocol XLFormValidatorProtocol <NSObject>
@required
-(XLFormValidationStatus *)isValid:(XLFormRowDescriptor *)row;
@end
XLFormRegexValidator是我们可以创建的验证器的一个示例。
一个非常常见的验证是确保值不为空或为nil。XLFom通过公开required
XLFormRowDescriptor属性来指定必要行。
要获取所有行的验证错误,我们可以调用以下XLFormViewController
方法
-(NSArray *)formValidationErrors;
XLFormRowDescriptor
允许我们配置UITableViewCell
的通用方面,例如:rowType
、label
、value
(默认值)、单元格是否是required
、hidden
或disabled
等。
您可能想要设置UITableViewCell
的另外一些属性。要设置其他属性,XLForm
使用键值编码,允许开发者通过keyPath设置单元格属性。
只需将属性添加到cellConfig
或cellConfigAtConfigure
字典属性中,这些属性属于XLFormRowDescriptor
。cellConfig
和cellConfigAtConfigure
之间的主要区别在于属性设置的时间。每次单元格即将显示时都会设置cellConfig
属性。而另一方面,cellConfigAtConfigure
在单元格的init方法被调用后立即设置属性,并且只设置一次。
自版本3.3.0起,您还可以使用cellConfigForSelector
来配置在查看选择行时XLFormOptionsViewController
的单元格的外观。
例如,如果您想设置占位符,可以按照以下方法操作
row = [XLFormRowDescriptor formRowDescriptorWithTag:@"title" rowType:XLFormRowDescriptorTypeText];
[row.cellConfigAtConfigure setObject:@"Title" forKey:@"textField.placeholder"];
[section addFormRow:row];
让我们看看如何更改单元格标签的颜色
Objective C
row = [XLFormRowDescriptor formRowDescriptorWithTag:@"title" rowType:XLFormRowDescriptorTypeText];
[row.cellConfig setObject:[UIColor redColor] forKey:@"textLabel.textColor"];
[section addFormRow:row];
Swift
row = XLFormRowDescriptor(tag: "title", rowType: XLFormRowDescriptorTypeText, title: "title")
row.cellConfig.setObject(UIColor.blackColor(), forKey: "backgroundColor")
row.cellConfig.setObject(UIColor.whiteColor(), forKey: "textLabel.textColor")
section.addFormRow(row)
将表单显示时设置第一个响应者与将属性assignFirstResponderOnShow
设置为YES
一样简单。默认情况下,该属性的值为NO
。
@property (nonatomic) BOOL assignFirstResponderOnShow;
您应在相关的XLFormRowDescriptor
实例的value
属性中设置。
@property (nonatomic) id value;
您可能会注意到value
属性的类型是id
,您有责任用适当类型设置值。例如,您应该将NSString
值设置到XLFormRowDescriptorTypeText
的XLFormRowDescriptor
实例中。
如果行已经显示,您可能需要更新单元格以查看UI更改。
XLFormViewController
提供了一个-(void)reloadFormRow:(XLFormRowDescriptor *)formRow
方法来完成此操作。
您应该像如何为行设置值中所描述的那样操作。
XLForm有几种选择行列类型。几乎所有的它们都需要知道将要选择的哪些值。对于特定的XLFormRowDescriptor
实例,您将这些值设置为一个设置到selectorOptions
属性的NSArray
实例中。
@property NSArray * selectorOptions;
如果您想获取原始表单值,应调用XLFormDescriptor
的formValues
方法。这样做,您将获得一个包含所有表单值的字典。
每行的tag
属性值用作字典键。只有具有非niltag
值的XLFormROwDescriptor
值被添加到字典中。
您可能对用作端点参数的表单值感兴趣。在这种情况下,httpParameters
将很有用。
如果您需要其他内容,您可以逐行迭代...
Objective C
NSMutableDictionary * result = [NSMutableDictionary dictionary];
for (XLFormSectionDescriptor * section in self.form.formSections) {
if (!section.isMultivaluedSection){
for (XLFormRowDescriptor * row in section.formRows) {
if (row.tag && ![row.tag isEqualToString:@""]){
[result setObject:(row.value ?: [NSNull null]) forKey:row.tag];
}
}
}
else{
NSMutableArray * multiValuedValuesArray = [NSMutableArray new];
for (XLFormRowDescriptor * row in section.formRows) {
if (row.value){
[multiValuedValuesArray addObject:row.value];
}
}
[result setObject:multiValuedValuesArray forKey:section.multivaluedTag];
}
}
return result;
Swift
var results = [String:String]()
if let fullName = form.formRowWithTag(tag.fullName).value as? String {
results[tag.fullName] = fullName
}
您可以使用cellConfigAtConfigure
字典属性更改UITextField的长度。此值表示相对于表格视图单元格的百分比。
Objective C
[row.cellConfigAtConfigure setObject:[NSNumber numberWithFloat:0.7] forKey:XLFormTextFieldLengthPercentage];
Swift
row.cellConfigAtConfigure.setObject(0.7, forKey:XLFormTextFieldLengthPercentage)
**注意:**在使用XLFormRowDescriptorTypeTextView
时,这也可以应用于UITextView;只需将百分比设置为键XLFormTextViewLengthPercentage
。
您可以使用cellConfig
字典属性更改字体或任何其他表格视图单元格属性。XLForm将在表格视图单元格即将显示时设置cellConfig
字典的值。
Objective C
[row.cellConfig setObject:[UIColor greenColor] forKey:@"textLabel.textColor"];
[row.cellConfig setObject:[UIFont fontWithName:FONT_LATO_REGULAR size:12.0] forKey:@"textLabel.font"];
[row.cellConfig setObject:[UIFont fontWithName:FONT_LATO_REGULAR size:12.0] forKey:@"detailTextLabel.font"];
Swift
row.cellConfig.setObject(UIColor.whiteColor(), forKey: "self.tintColor")
row.cellConfig.setObject(UIFont(name: "AppleSDGothicNeo-Regular", size: 17)!, forKey: "textLabel.font")
row.cellConfig.setObject(UIColor.whiteColor(), forKey: "textField.textColor")
row.cellConfig.setObject(UIFont(name: "AppleSDGothicNeo-Regular", size: 17)!, forKey: "textField.font")
有关更多详细信息,请查看UICustomizationFormViewController.m示例。
####如何为日期单元格设置最小/最大值?
每个XLFormDateCell都有一个minimumDate
和maximumDate
属性。要将日期行设置为下三天内的值,您可以这样做:
Objective C
[row.cellConfigAtConfigure setObject:[NSDate new] forKey:@"minimumDate"];
[row.cellConfigAtConfigure setObject:[NSDate dateWithTimeIntervalSinceNow:(60*60*24*3)] forKey:@"maximumDate"];
Swift
row.cellConfig.setObject(NSDate(), forKey: "maximumDate")
可以使用disable
XLFormDescriptor属性来禁用整个表单。为了使显示的单元格生效,我们应该重新加载可见的单元格([self.tableView reloadData])。
在将disable
属性设置为YES
之后添加的任何行都将自动反映禁用模式(无需重新加载表视图)。
要隐藏一行或一个部分,您应设置其hidden属性。最简单的方法是将NSString设置给它。比如说,如果您想要一个部分在先前的布尔开关行被设置为1(或YES)时隐藏,您将这样做:
section.hidden = [NSString stringWithFormat:@"$%@ == 1", previousRow];
就这么简单!
与旧版本不兼容的只有XLFormRowDescriptor
的disabled
属性现在是id
。所以,您只需在设置的值前面加上@
,如下所示:
row.disabled = @YES; // before: row.disabled = YES;
覆盖inputAccessoryViewForRowDescriptor:
在 XLFormViewController
方法上。
如果您想要完全禁用它,可以返回nil。但您也可以在这里自定义其整个外观。
- (UIView *)inputAccessoryViewForRowDescriptor:(XLFormRowDescriptor *)rowDescriptor
{
return nil; //will hide it completely
// You can use the rowDescriptor parameter to hide/customize the accessory view for a particular rowDescriptor type.
}
将被推入的视图控制器必须遵守XLFormRowDescriptorViewController
协议,该协议包含以下属性:
@property (nonatomic) XLFormRowDescriptor * rowDescriptor;
此rowDescriptor指向先前视图控制器选中的行,并在过渡到新控制器之前设置,以便在例如其viewDidLoad
方法中可以使用。这就是该视图控制器应该设置的地方。
最好的办法是扩展该单元格的类并覆盖其update和/或configure方法。为了使这生效,您还应该更新XLFormViewController的子类中的cellClassesForRowDescriptorTypes
字典,将自定义类设置为要更改的单元格的类。
要更改单元格的returnKeyType,您可以设置returnKeyType
和nextReturnKeyType
属性。前者在没有启用导航或没有后续行的条件下使用。在其他情况下使用后者。
如果您创建了自定义单元格并想使用这些,您应遵循XLFormReturnKeyProtocol
协议。
设置方法如下
[row.cellConfigAtConfigure setObject:@(UIReturnKeyGo) forKey:@"nextReturnKeyType"];
如果您想更改同一类中所有单元格的高度,应将该单元格子类化并覆盖类方法formDescriptorCellHeightForRowDescriptor
。
如果您想要更改单个单元格的高度,可以将该高度设置为XLFormRowDescripto的height
属性,如下所示:
XLFormRowDescriptor* row = ...
row.height = 55;
要更改XLFormOptionsViewController的单元格外观,您可以在行描述符上使用cellConfigForSelector
属性。
示例
[row.cellConfigForSelector setObject:[UIColor redColor] forKey:@"textLabel.textColor"];
您可以使用textFieldMaxNumberOfCharacters
和textViewMaxNumberOfCharacters
分别实现这一点。
[row.cellConfigAtConfigure setObject:@(20) forKey:@"textViewMaxNumberOfCharacters"];
通常,master分支包含最新功能和最新修复。另一方面,这些功能尚未完全测试,并且master上的更改可能会随时发生。出于上述原因,我强烈建议您分发此存储库,并自行从master管理更新,根据需要进行适当的拉取操作。
要使用 xmartlabs master 分支......
pod 'XLForm', :git => 'https://github.com/xmartlabs/XLForm.git'
如果您愿意,您可以将存储库URL替换为您分叉版本的URL。
如果您已使用cocoapods安装了XLForm,并在Podfile中设置了use_frameworks!
,您可以在任何Swift文件中添加import XLForm
。
如果您正在使用 Cocoapods,但在 Podfile 中没有设置 use_frameworks!
,请将 #import <XLForm/XLForm.h>
添加到您的桥接头文件中。
有关如何创建和配置桥接头文件的更多详细信息,请访问 将 Objective-C 导入 Swift。
$ git submodule add https://github.com/xmartlabs/XLForm.git
打开由上一个 git 子模块命令创建的 XLForm 文件夹,将 XLForm.xcodeproj 拖拽到您的应用程序 Xcode 项目的项目导航器中。
在项目导航器中选择 XLForm.xcodeproj,并验证其部署目标与您的应用程序部署目标相匹配。
在 Xcode 导航中选择您的项目,然后从侧边栏中选择您的应用程序目标。接下来,选择“通用”选项卡,并点击“嵌入式二进制文件”部分下的 + 按钮。
选择 XLForm.framework
,操作完成!
请查看 CHANGELOG
有任何建议或问题?请创建 Github 问题或联系我们。