测试已测试 | ✓ |
Lang语言 | Obj-CObjective C |
许可证 | MIT |
发布时间最后发布 | 2017年10月 |
由 felixyin 维护。
由 XMARTLABS 提供。
如果您正在寻找 Swift 原生实现,我们最近创建了 Eureka,这是在 Swift 中对 XLForm 的全新设计。 不要担心,我们仍然会维护和改进 XLForm,Objective-C 仍然很棒!
XLForm 是最灵活和强大的 iOS 库,用于创建动态表视图表单。该库的目标是通过手动表单获得相同的功能,同时只需消耗 1/10 的时间。
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 没有依赖其他库,但示例项目使用一些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
的表示。
####选择行
选择行允许我们从列表中选择一个或多个值。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
行属性作为显示文本。XLFormRowDescriptor
实例具有 valueTransformer
属性值。XLForm 使用 NSValueTransformer
将选定的对象转换为 NSString。NSString
或 NSNumber
,则使用对象的 description
属性。XLFormOptionObject
,XLForm 从 formDisplayText
方法获取显示值。:)
。您可能会通过设置 noValueDisplayText
或 valueTransformer
属性或使选择选项对象符合 XLFormOptionObject
协议来更改显示文本。
这是协议声明
@protocol XLFormOptionObject <NSObject>
@required
-(NSString *)formDisplayText;
-(id)formValue;
@end
####日期和时间行
XLForms 支持三种日期类型: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
,允许我们在选择时执行任何操作。它可以通过块(闭包)、选择器、segue标识符、segue类或指定要显示的视图控制器来配置。视图控制器指定可以通过设置视图控制器类、视图控制器Storyboard Id或nib名称来完成。nib名称必须与视图控制器类名称匹配。
任何 XLFormSectionDescriptor
对象都可以设置以支持行插入、删除或重新排序。可以启用这些模式之一、组合或全部启用。
多值部分只是一个支持上述任一模式的部分。
多值 XLFormSectionDescriptor
最有趣的部分是它支持在 Rows 部分显示的所有类型以及自定义行。
创建多值部分非常简单,只需使用以下便捷的 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
为 XLFormSectionDescriptor
中每个没有设置 multivaluedTag
值的 XLFormRowDescriptor
添加一个值。字典的键是 XLFormRowDescriptor
的 tag
属性值。
对于具有 multivaluedTag
值的每个部分,XLForm 添加一个以 NSArray
作为值的项目,数组中的每个值是该部分中的每行的值,键是 multivaluedTag
。
例如,如果我们有一个 multivaluedTag
属性值等于 tags
的部分和包含在行中的以下值:'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
创建自定义单元格后,需要让 XLForm
了解此单元格,通过将行定义添加到 cellClassesForRowDescriptorTypes
字典中来实现。
[[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
值更新视图,以及从自定义选择器视图控制器中设置选中值。
注意:属性
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
要容易得多,标签应该是唯一的,并且在添加、修改或删除tableview时不会改变。
要注意的是,所有对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
),当前一行(first
)包含“隐藏”值时使其消失。
second.hidden = [NSString stringWithFormat:@"$%@ contains[c] 'hide'", first];
这将在此处的'$'之后插入第一个标签,当然您也可以手动完成。当进行谓词评估时,每个标签变量都会被对应的行描述符替换。
当参数是NSString时,除非标签后面跟'.isHidden'或'.isDisabled',否则每个标签都会附加'.value'。这意味着一行(或一个部分)可能取决于另一行的value
或hidden
或disabled
属性。当直接使用NSPredicate设置属性时,其formatString不会被更改(因此如果您想引用其值,则您必须在每个变量之后附加'.value')。设置NSString是最简单的方法,但对于某些复杂的谓词可能不会工作,因此对于这些谓词,您应该直接设置一个NSPredicate。
您还可以使用布尔对象设置此属性,这意味着除非手动设置,否则属性的值不会改变。
要获取评估后的布尔值,应调用isHidden
方法。每次调用它时,不会重新评估谓词,但仅当它依赖的行的值(或隐藏/禁用状态)发生变化时才评估。当发生这种情况并且返回值发生变化时,它将自动反映这种变化,因此不需要调用其他任何方法。
这里有一个更复杂的例子
可以禁用行,这样用户就无法更改它们。默认情况下,禁用的行的文本颜色为灰色。要禁用行,只需设置其禁用属性即可。
@property id disabled;
此属性期望一个包含BOOL、NSString或NSPredicate的NSNumber。布尔值将静态地禁用(或启用)行。其他两个属性与上面章节中解释的隐藏属性工作原理相同。这意味着行可以根据其他行的值禁用或启用。当设置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
等。
您可能想设置单元格的另一些属性。要设置其他属性,XL
使用键值编码允许开发者通过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
实例中。
如果行已经显示,则可能需要更新单元格以查看界面更改。
由 XLFormViewController
提供的 -(void)reloadFormRow:(XLFormRowDescriptor *)formRow
方法可用于执行此操作。
您应该像如何设置行值中描述的那样执行。
XLForm 有几种选择行类型。几乎所有这些类型都需要知道将要选择的值。对于特定的 XLFormRowDescriptor
实例,您可以设置这些值,将 NSArray
实例设置到 selectorOptions
属性中。
@property NSArray * selectorOptions;
如果您想获取原始表单值,应该调用 XLFormDescriptor
的 formValues
方法。这样做您将获得一个包含所有表单值的字典。
每行的 tag
属性值用作字典键。只有非空 tag
值的 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
的表单后添加任何其他行,这些行将自动反映禁用模式(无需重新加载表视图)。
要隐藏一行或一个部分,你应该设置其隐藏属性。最简单的方法是将一个 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;
此行描述符引用前一个视图控制器中选中的行,将在转换到新控制器之前设置,以便在示例中,例如在它的 viewDidLoad
方法中可访问。这就是该视图控制器应该设置的地方。
最好的方法是扩展该单元格的类并重写其更新和/或配置方法。为了使这起作用,你还应该更新 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 文件夹,将其拖放到您的应用程序的 Xcode 项目的项目导航器中。
在项目导航器中选择 XLForm.xcodeproj,并验证部署目标与您的应用程序部署目标匹配。
在 Xcode 导航中选择您的项目,然后在侧边栏中选择您的应用程序目标。接下来选择“通用”选项卡,然后在“已嵌入的二进制文件”部分下点击 + 按钮。
选择 XLForm.framework
,我们会完成!
查看 CHANGELOG
有任何建议或问题?请创建一个 Github 问题或与我们联系。