UIKit当仓央嘉措遇上纳兰性德iOS从入门到放弃

XLForm框架学习修改笔记第一篇

2017-03-31  本文已影响1961人  继续向前冲

工作中马上要开的新项目,需要深度使用更改XLForm框架,所以结合着去年学习的一点知识,这次要对它开始大搞,第一篇是翻译github文档。如果读者看到不对的地方,请多做自我批评哈😉
为大家献上原文地址XLForm

xlform是最灵活和强大的iOS库来创建动态的表观形态。该图书馆的目标是获得相同的权力,手工制作的形式,但花费1 / 10的时间。
xlform提供了一个非常强大的DSL(领域特定语言)用于创建表格。它在运行时跟踪这个规范,更新用户界面。

让我们看看iOS日历事件的形式创建使用xlform

XLForm.gif

XLForm是做什么的

基于声明形式定义加载窗体。
跟踪运行时的定义更改以更新表单接口。此自述的动态表单部分的进一步信息。
支持多值部分,允许我们创建,删除或重新排序行。更多细节见 Multivalued Sections section bellow.。
支持自定义行。
支持自定义选择器。
提供多个内联选择器,如日期选择器和选择器内联选择器,并提供创建自定义内联选择器的方法。
基于表单定义的表单数据验证。
能够轻松地在行之间导航,完全可定制。
如果需要显示inputaccessoryview能力。默认情况下,方便导航到下个视图。
特定行或整个窗体的只读模式。
可以根据其他行值隐藏或显示行。这可以通过使用nspredicates声明。

如何创建表单

XLFormViewController创建的实例

#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

使用initializeform方法

创建一个表单应该声明它通过xlformdescriptor实例并把它分配给xlformviewcontroller实例。
定义一个窗体,我们使用3个类:
XLFormDescriptor
XLFormSectionDescriptor
XLFormRowDescriptor
一种表单定义是一个xlformdescriptor实例包含一个或多个部分(xlformsectiondescriptor实例),每部分包含几行(xlformrowdescriptor实例)。你可能已经注意到DSL结构模拟的一个表格的结构(Table -->> Sections -- >> Rows)。由此产生的表格视图的结构(sections and rows order))反映了定义的结构

让我们看看一个例子实现initializeform定义iOS日历事件表

- (void)initializeForm {
  XLFormDescriptor * form; //模拟tableview结构
  XLFormSectionDescriptor * section;//模拟section结构
  XLFormRowDescriptor * row;//模拟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或其他的复杂性。(PS:有了他你可以比tableview程序员知道的还少哈,就可以搞出好大的事情来)
想看更多的例子,各位童鞋可以下载代码自己跑一下,XLForm并不完全依赖pods 也就是说把代码直接拽进去就👌了,有没有感觉很爽。

在Storyboards上使用XLForm

执行完如何创建一个表单
然后在Storyboard 上拖放一个UIViewController
在UIViewController上绑定好类

如何运行XLForm示例代码

这里就不说太多废话了,搞ios的肯定都知道怎么了运行程序了。

输入行

XLForm-Inputs.gif

输入行允许用户输入文本值。基本上他们使用UITextView UITextField或控制。输入行类型之间的主要区别是键盘类型,keyboardType、autocorrectionType、autocapitalizationType

static NSString *const XLFormRowDescriptorTypeText = @"text";

将一个UITextField 由UITextAutocorrectionTypeDefault表示,uitextautocapitalizationtypesentences和UIKeyboardTypeDefault

static NSString *const XLFormRowDescriptorTypeName = @"name";

将一个UITextField 由 UITextAutocorrectionTypeNo表示,uitextautocapitalizationtypewords和UIKeyboardTypeDefault。

static NSString *const XLFormRowDescriptorTypeURL = @"url";

将一个UITextField 由 UITextAutocorrectionTypeNo表示,uitextautocapitalizationtypewords和UIKeyboardTypeURL。

static NSString *const XLFormRowDescriptorTypeEmail = @"email";

将一个UITextField 由 UITextAutocorrectionTypeNo表示,uitextautocapitalizationtypewords和UIKeyboardTypeEmailAddress。

static NSString *const XLFormRowDescriptorTypePassword = @"password";

将一个UITextField由 UITextAutocorrectionTypeNo表示,uitextautocapitalizationtypenone和uikeyboardtypeasciicapable。这排式也将securetextentry是为了隐藏什么用户类型。

static NSString *const XLFormRowDescriptorTypeNumber = @"number";

将一个UITextField 由 UITextAutocorrectionTypeNo表示,uitextautocapitalizationtypewords和UIKeyboardTypeNumbersAndPunctuation。

static NSString *const XLFormRowDescriptorTypePhone = @"phone";

将一个UITextField 由 UIKeyboardTypePhonePad表示.

static NSString *const XLFormRowDescriptorTypeTwitter = @"twitter";

将一个UITextField 由 UITextAutocorrectionTypeNo表示,uitextautocapitalizationtypewords和UIKeyboardTypeTwitter。

static NSString *const XLFormRowDescriptorTypeAccount = @"account";

将一个UITextField 由 UITextAutocorrectionTypeNo表示,uitextautocapitalizationtypewords和UIKeyboardTypeDefault。

static NSString *const XLFormRowDescriptorTypeInteger = @"integer";

将一个UITextField 由 UITextAutocorrectionTypeNo表示,UIKeyboardTypeNumberPad。

static NSString *const XLFormRowDescriptorTypeDecimal = @"decimal";

将一个UITextField 由 UITextAutocorrectionTypeNo表示,UIKeyboardTypeDecimalPad

static NSString *const XLFormRowDescriptorTypeTextView = @"textView";

将一个UITextView 由 UITextAutocorrectionTypeNo表示,UIKeyboardTypeDecimalPad和 UIKeyboardTypeDefault。

选择行

选择器行允许我们从列表中选择值或值。xlform支持8种类型的选择器开箱:

XLForm-Selectors.gif
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遵循以下规则来显示一个对象:
1.如果对xlformrowdescriptor对象的值是nil,xlform采用novaluedisplaytext行属性作为显示文本。
2.如果xlformrowdescriptor实例有一个valuetransformer属性值。xlform采用nsvaluetransformer将选定的对象到一个NSString。
3.如果对象是一个NSString或者NSNumber将对象description描述属性。
4.如果对象符合协议xlformoptionobject,XLForm从formdisplaytext方法获取显示值。
5.如果你不遵守协议,会返nil。
你可能感兴趣的改变显示的文本设置novaluedisplaytext或valuetransformer财产或使选择器选择对象符合xlformoptionobject协议。
这是协议声明:

@protocol XLFormOptionObject <NSObject>

@required
-(NSString *)formDisplayText;
-(id)formValue;

@end

日期和时间行

xlforms支持日期的3种类型:日期,日期,时间和倒计时,它能够以2种不同的方式呈现uidatepicker控制,内联和非内联。

XLForm-Dates.gif
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";

下面是如何定义这些行类型的示例:

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];

Boolean Rows

xlforms支持2种类型的布尔控件:

XLForm-Boolean.gif
static NSString *const XLFormRowDescriptorTypeBooleanCheck = @"booleanCheck";
static NSString *const XLFormRowDescriptorTypeBooleanSwitch = @"booleanSwitch";

我们还可以使用选择器行部分中引入的任何选择器行类型模拟其他类型的Boolean Rows。

其他行

xlforms支持使用uistepper计数控制:

XLForm-stepCounter.gif
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"];

Slider

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"];

设置@(0)禁用steps功能。

Info

有时我们的应用程序需要显示不可编辑的数据。xlform为我们提供了xlformrowdescriptortypeinfo排式不可编辑的信息显示。一个例子的使用将显示应用程序版本的设置部分的应用程序。

Button

除了数据输入行,不可编辑的行选择器,xlform一键排xlformrowdescriptortypebutton允许我们选择时做任何动作。它可以被配置为使用一个block、selector、一个segue标识符,segue类或具体一个视图控制器被提出。ViewController的说明可通过设置视图控制器类,storyboard 或者 xib name。xib name必须匹配视图控制器类名。

多节情况下动态增加,删除,排序行

任何xlformsectiondescriptor对象都可以动态插入、删除或排序。可以只支持其中一种模式,也可以全面支持或者组合支持。
如果存在多节的话就支持其中之一的部分。
多值xlformsectiondescriptor最有趣的部分是,它支持显示行中的部分以及自定义排所有类型。

XLForm-MultiValuedSections.gif

如何创建多节表格

创建一个多部分使用下列简单方便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有2种不同的插入方式: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增加每个xlformrowdescriptor值,属于xlformsectiondescriptor没有multivaluedtag值设置;dictionary是xlformrowdescriptor标签属性的值。
每个部分有一个multivaluedtag值,XLForm增加了一个dictionary的项目以NSArray作为值,NSArray的每个值都包含在列每行的值,而关键是multivaluedtag。
例如,如果我们用multivaluedtag属性等于标签和下面的值包含的行有一段: 'family', 'male', 'female', 'client',所产生的价值将标签:['family', 'male', 'female', 'client']。

httpParameters

在相同的情况下,我们需要形成价值可能不同于xlformrowdescriptor实例的值。这通常是selectors row的情况,当我们需要将窗体值发送到某些端点时,所选值可能是核心数据对象或任何其他对象。在这种情况下xlform需要知道如何获得和所选对象的描述值。
当使用-(NSDictionary *)httpparameters方法,xlform遵循以下规则得到xlformrowdescriptor值:
1.如果对象是NSString, NSNumber or NSDate, 那么值就是他本身。
2.如果对象符合协议xlformoptionobject,XLForm从formvalue方法获得的值。
3.如果不符合上述两点,则返nil
multivaluedtag以同样的方式在formvalues方法运行。

如何创建自定义行

创建一个自定义的cell,你需要创建一个xlformbasecell他是扩展的 UITableViewCell。xlformbasecell遵循xlformdescriptorcell协议。
你可能对自定义cell比较感兴趣

@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

一旦一个自定义cell已经建立,cellclassesforrowdescriptortypes 添加这个cell的定义。

[[XLFormViewController cellClassesForRowDescriptorTypes] setObject:[MYCustomCellClass class] forKey:kMyAppCustomCellType];

或者,如果我们使用nib文件来定义XLBaseDescriptorCell:

[[XLFormViewController cellClassesForRowDescriptorTypes] setObject:@"nibNameWithoutNibExtension" forKey:kMyAppCustomCellType];

这样做,当需要使用到自定义的cell的时候XLForm能够正确的进行初始化。

在视图控制器中自定义行选择器

基本上基础selector的能够满足我们的日常开发需要,但有时我们需要更多的灵活性,以更好的用户体验给用户或做一些不支持默认。
比如说你的应用程序用户需要选择一个地图坐标或者它需要从服务器端选择一个值。如何简单的实现(PS:其实说来这么多我认为做就是想说控制器之间的值的回调问题)

XLForm-map-custom-selector.gif

定义前面的选择器行是那么简单…

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建立rowdescriptor属性使用xlformrowdescriptor实例属于选择行。
开发者有责任在一个自定义的选择视图控制器中更新rowDescriptor页面的值以及我们选择的值。

备注: viewControllerClass, viewControllerNibName or viewControllerStoryboardI 的属性值相互独立 被使用在XLFormButtonCell and XLFormSelectorCell。如果创建自定义cell,则负责使用它们。

另一个例子

XLForm-dynamic-custom-selector.gif
row = [XLFormRowDescriptor formRowDescriptorWithTag:kSelectorUser rowType:XLFormRowDescriptorTypeSelectorPush title:@"User"];
row.action.viewControllerClass = [UsersTableViewController class];

你可以在工程中发现更多的例子Examples/Objective-C/Examples/Selectors/CustomSelectors/ and Examples/Objective-C/Examples/Selectors/DynamicSelector.

动态表格-如何在运行时动态更改表格

任何在xlformdescriptor的更改都将反映在xlformviewcontroller 的TableView上。这意味着,当一段或一行添加或去除xlform将已动画的形式在节或行响应。
我们再也不用处理NSIndexPaths 或者增加,删除UITableViewCell。一个特定的tableviewcell变化沿时间,这使得让很难通过nsindexpath 来维护了。
设计xlform的时候为 xlformrowdescriptor行都增加了tag的属性,XLFormDescriptor可以通过tag获得需要操作的XLFormRowDescriptor。它很容易使用xlformrowdescriptors标签管理,标签应该是唯一的,它不会改变对TableView补充修改或删减。
记住所有的表格形式的修改必须使用描述符不修改直接在表格是很重要的。
通常,当某些值更改或某些行或节被添加或移除时,您可能需要更改窗体。为此,可以设置行或段的禁用和隐藏属性。有关更多细节,请参见依赖于其他行值的行或段不可见。
为了保持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允许你定义之间的依赖关系,从而使另一行自动改变。例如,你可能有一个表单,如果用户有宠物,你会向用户提问。如果答案是“是”,你可能想问他们的名字如何。因此,您可以根据其他行的值重新生成一行不可见和再次可见的行。同样发生的部分。看看下面的例子:

XLFormPredicatesBasic.gif

当然,你也可以通过观察一些行的值,并相应地删除和添加行来手动完成这项工作,但是这已经是一个已经完成的工作了。

它是如何工作的

为了使行和节自动出现和消失,每个描述符中都有一个属性:

@property id hidden;

这个ID的对象通常是一个或一个nspredicate NSNumber含有一个bool。它可以使用任何人或最终一个NSString的nspredicate将被创建。为了解决这个字符串要语法正确。
例如,当前一行(第一)包含值“隐藏”时,可以将下面的字符串设置为行(第二个),以使其消失。

second.hidden = [NSString stringWithFormat:@"$%@ contains[c] 'hide'", first];

这将插入标签后的第一个' $ ',你也可以手动,当然。当predicate被求值时,每个标记变量将被相应的行描述符替换。
当参数是一个NSString,'.value'将被添加到每个标签,除非标签其次是'.isHidden' 或'.isDisabled'。这意味着行(或节)可能依赖于另一行的value或hidden或disabled属性。当属性通过一个nspredicate设置值时,它则不会改变(所以你要追加的 '.value',每个变量,如果你想把它的value)。设置一个NSString是最简单的方法,但一些复杂的谓词可能不工作,所以对于那些你应该直接设置一个nspredicate。
你也可以使用这个特性的一个bool对象意味着该属性的值不会改变除非手动设置。
要讨论的isHidden方法是一个boolean值。谓词每次调用时不会重新获取,但是当(hidden/disabled状态)发生变化时它将自动反映变化的形式,没有其他的方法必须调用。(PS:英语水平实在有限,感觉就是一旦在属性上设置好方法后,会自动监听需要改变的行的状态,当值变化后悔自动触发,这明显就是就是KVO实现的嘛,说的真心有点绕)

这里是另一个例子,这次有点复杂:

XLFormPredicates.gif

禁用行(设置为只读模式)

可以禁用行,以便用户不能更改它们。默认情况下禁用行具有灰色文本颜色。要禁用行,唯一要做的就是设置它的禁用属性:

@property id disabled;

此属性预计是一个NSNumber其中包含了a BOOL, a NSString or a NSPredicate. 一个bool将静态禁用(或使行)。其他两个工作就像隐藏的属性解释在上述部分。这意味着可以根据其他行的值禁用和启用行。当NSString将a nspredicate产生以字符串格式的字符串,它具有一致的目的。
与隐藏属性的区别在于,检查行的禁用状态不会自动反映窗体上的值。的xlformviewcontroller的updateformrow方法应该叫tharefore。

校验器

我们可以验证表单数据使用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的验证器例子。
一个最普通的validation是确保值不为空或nil。xlfom暴露所需的xlformrowdescriptor属性指定所需的行。
得到所有行验证错误我们可以调用以下xlformviewcontroller方法:

-(NSArray *)formValidationErrors;

行的附加配置

xlformrowdescriptor允许我们配置一个UITableViewCell,通用方面例如:rowType,label,value(默认值),如果cell是required,hidden或disabled等等。
你可能想要建立的UITableViewCell的另一个特性。通过键值编码的方式允许开发人员添加附加配置。
你只需要去cellconfig或xlformrowdescriptor cellconfigatconfigure字典属性添加属性。cellconfig和cellconfigatconfigure之间的主要区别是时属性设置。cellconfig属性设置一个cell每次即将显示。cellconfigatconfigure,设置属性后的cell 执行init方法称为只有一次。
自从版本3.3.0也可以使用cellconfigforselector配置如何的xlformoptionsviewcontroller像cell当它显示为一个选择器行。
例如,如果你想设置placeholder,你可以做以下:

row = [XLFormRowDescriptor formRowDescriptorWithTag:@"title" rowType:XLFormRowDescriptorTypeText];
[row.cellConfigAtConfigure setObject:@"Title" forKey:@"textField.placeholder"];
[section addFormRow:row];

让我们看看如何更改单元格标签的颜色:

row = [XLFormRowDescriptor formRowDescriptorWithTag:@"title" rowType:XLFormRowDescriptorTypeText];
[row.cellConfig setObject:[UIColor redColor] forKey:@"textLabel.textColor"];
[section addFormRow:row];

终于大功告成了,真是搞了好久,后面还有常见问题没有细看,等以后的篇幅慢慢搞吧,这个第一次搞这么长的文章,正感觉刚不住的感觉,肯定有不少有问题的,在后续我会慢慢改的哈😁。

上一篇 下一篇

猜你喜欢

热点阅读