美文网首页iOSios_UIiOS从入门到放弃
iOS复杂UITableViewCell表单实现 - XLFor

iOS复杂UITableViewCell表单实现 - XLFor

作者: Mob_Developer | 来源:发表于2017-03-29 01:44 被阅读3199次

    XLForm

    目的

    XLForm是创建动态表格视图最灵活、最强大的iOS库。 本库的目的是像手动创建一样,实现表单功能,但是只用其1/10的时间。

    XLForm提供了非常强大的DSL(域特定语言)以创建表单。 它在运行时遵从此规范,即时更新UI。

    XLForm的功能

    • 根据表单定义的陈述,加载表单。
    • 在运行时跟踪表单定义更改,相应地更新表单界面。
    • 支持多值分组,允许我们创建,删除或对行排序。有关详细信息,请参考下面的“多值分组”部分。
    • 支持自定义行。
    • 支持自定义选择器。有关如何自定义选择器的更多细节,请查看“自定义选择器”部分。
    • 提供了几个内联选择器,比如日期选择器和选择器内联选择器,并提供创建自定义内联选择器的方法。
    • 基于表单定义的表单数据验证。
    • 能够轻松地在行之间导航,可高度定制。
    • 如果需要,可以显示inputAccessoryView。默认情况下,显示inputAccessoryView。
    • 可为特定行或整个表单提供只读模式。
    • 行可以根据其它行的值以隐藏或显示。可以使用NSPredicates声明性地完成。 (请参阅根据其他行值使行或分组不可见)

    怎样创建表单

    #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实例。如我们所说XLForm基于DSL,它在隐藏了复杂的东西的同时,实现了手工表单的功能和灵活性。

    我们使用三个类定义表单

    • XLFormDescriptor
    • XLFormSectionDescriptor
    • XLFormRowDescriptor

    一个表单的定义就是一个XLFormDescriptor实例,这个实例包含一个或多个分组(XLFormSectionDescriptor实例),每个分组包含多行(XLFormRowDescriptor实例)。您可能已经注意到DSL结构类似于UITableView(表 - >>分组 - >>行)的结构。 生成的表格视图表单的结构(分组和行的顺序)反映了表单定义的结构。

    ** 看下面initializeForm实现以定义iOS日历事件表单的例子 **

    - (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不依赖其它pod,还有,这些示例项目使用一些Cocoapod来显示高级XLForm功能。

    ** 输入行 **

    输入行允许用户输入文本值。通常使用UITextFieldUITextView。输入行类型之间最大的区别是keyboardType, autocorrectionType 和 autocapitalizationType 配置

    static NSString *const XLFormRowDescriptorTypeText = @"text";
    

    将由UITextAutocorrectionTypeDefaultUITextAutocapitalizationTypeSentencesUIKeyboardTypeDefaultUITextField表示。

    static NSString *const XLFormRowDescriptorTypeName = @"name";
    

    将由UITextAutocorrectionTypeNo, UITextAutocapitalizationTypeWordsUIKeyboardTypeDefault的UITextField表示。

    static NSString *const XLFormRowDescriptorTypeURL = @"url";
    

    将由UITextField with UITextAutocorrectionTypeNo, UITextAutocapitalizationTypeNoneUIKeyboardTypeURL的UITextField表示。

    static NSString *const XLFormRowDescriptorTypeEmail = @"email";
    

    将由UITextAutocorrectionTypeNo, UITextAutocapitalizationTypeNoneUIKeyboardTypeEmailAddress的UITextField表示。

    static NSString *const XLFormRowDescriptorTypePassword = @"password";
    
    

    将由UITextAutocorrectionTypeNo, UITextAutocapitalizationTypeNoneUIKeyboardTypeASCIICapable的UITextField表示。这个类型的行同时会设置secureTextEntry为YES,以隐藏用户输入的内容。

    static NSString *const XLFormRowDescriptorTypeNumber = @"number";
    

    将由UITextAutocorrectionTypeNo, UITextAutocapitalizationTypeNoneUIKeyboardTypeNumbersAndPunctuation的UITextField表示。

    static NSString *const XLFormRowDescriptorTypePhone = @"phone";
    

    将由UIKeyboardTypePhonePad的UITextField表示。

    static NSString *const XLFormRowDescriptorTypeTwitter = @"twitter";
    

    将由UITextAutocorrectionTypeNo, UITextAutocapitalizationTypeNoneUIKeyboardTypeTwitter的UITextField表示。

    static NSString *const XLFormRowDescriptorTypeAccount = @"account";
    

    将由UITextAutocorrectionTypeNo, UITextAutocapitalizationTypeNone and UIKeyboardTypeDefault的UITextField表示。

    static NSString *const XLFormRowDescriptorTypeInteger = @"integer";
    

    将由UIKeyboardTypeNumberPad的UITextField表示。

    static NSString *const XLFormRowDescriptorTypeDecimal = @"decimal";
    

    将由UIKeyboardTypeDecimalPad的UITextField表示。

    static NSString *const XLFormRowDescriptorTypeTextView = @"textView";
    

    将由UITextAutocorrectionTypeDefault, UITextAutocapitalizationTypeSentencesUIKeyboardTypeDefault的UITextField的UITextField表示。

    Selector Rows

    Selector Rows允许我们从列表中选择一个或多个值。XLForm支持8种直接可用的Selector类型

    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必须能够显示这些对象。

    XFForm在显示对象时遵循以下规则:

    1. 如果XLFormRowDescriptor对象为nil, XLForm使用行属性noValueDisplayText作为显示文本。
    2. 如果XLFormRowDescriptorvalueTransformer属性值,XLForm 使用NSValueTransformer将选择的对象转换成字符串。
    3. 如果对象是NSString或者NSNumber, XLForm使用其description属性。
    4. 如果对象遵从XLFormOptionObject协议,XLForm从formDisplayText获取显示值。
    5. 否则返回nil,意思是你应该遵从这个协议 :)

    您可能有兴趣通过设置noValueDisplayText或valueTransformer属性或使选择器选项对象符合XLFormOptionObject协议来更改显示文本。

    下面是这个协议声明:

    @protocol XLFormOptionObject <NSObject>
    
    @required
    -(NSString *)formDisplayText;
    -(id)formValue;
    
    @end
    

    日期和时间行

    XLForm支持三种日期:Date, DateTime , Time and Countdown Timer,并且可以使用2中方式展示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";
    ```
    
    这里有怎样定义这些行类型的例子:
    ```
    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
    XLForm支持两种开关控制
    ```
    static NSString *const XLFormRowDescriptorTypeBooleanCheck = @"booleanCheck";
    ```
    ```
    static NSString *const XLFormRowDescriptorTypeBooleanSwitch = @"booleanSwitch";
    ```
    我们也能够使用在**Selector Rows **里介绍的任意Seector Row来模拟其他类型的Boolean Rows,。
    
    ## 其他行
    
    **Stepper**
    
    XLForms支持UIStepper计数
    ```
    static NSString *const XLFormRowDescriptorTypeStepCounter = @"stepCounter";
    ```
    
    你能够轻松的设置stepper参数:
    ```
    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";
    ```
    你能够依据个人喜好轻松的调整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)`以禁用步进功能。
    
    **info**
    有时应用需要显示不可编辑的数据。XLForm提供了`XLFormRowDescriptorTypeInfo`行类型以显示不可编辑的信息。比如在应用的设置部分显示应用版本。
    
    **Button**
    除了数据输入行,不可编辑的行和选择器,XLForm有一个按钮行XLFormRowDescriptorTypeButton,允许我们在选择时执行任何操作。 它可以使用块(clousure),选择器,segue标识符,segue类或指定要呈现的视图控制器进行配置。 ViewController格式可以通过设置视图控制器类,视图控制器故事板Id或nib名称来完成。 Nib名称必须与视图控制器类名称匹配。
    
    ## Multivalued Sections (插入、删除行以及排序)
    任意的`XLFormSectionDescriptor`实例都支持插入、删除行以及排序。可以使用其中一种或者混合或者同时使用所有的模式。
    
    多值XLFormSectionDescriptor最有趣的部分是它支持在“行”分组上显示的所有行类型和自定义行。
    
    ** 怎样创建多值分组 **
    创建多值分组和使用下面`XLFormSectionDescriptor`的initializer一样简单:
    
    ```
    +(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];
    ```
    ## 表单值
    ** formValues **
    你可以调用`-(NSDictionary *)formValues`获取所有的表单值;`XLFormViewController`实例或者`XLFormDescriptor`实例。
    
    返回的字典遵循以下规则:
    
    XLForm为每个属于没有设置multivaluedTag值的XLFormSectionDescriptor的XLFormRowDescriptor添加一个值。 字典键是XLFormRowDescriptor的tag属性的值。
    
    有multivaluedTag值的每个分组,XLForm会添加用NSArray作为值的字典,数组中的每个值都是该分组中包含的每一行的值,multivaluedTag是其键。
    
    例如,如果我们有一个multivaluedTag属性等于tags的分组,包含的行上是以下值:'family','male','female','client',生成的值将是tags:['family ','male','female','client']
    
    ** httpParameters**
    
    在同样的情况下,我们需要的表单值可能与XLFormRowDescriptor实例的值不同。 这通常是选择器行的情况,当我们需要将表单值发送到某个终端时,所选择的值可以是Core Data对象或其他任意对象。 在这种情况下,XLForm需要知道如何获取所选对象的值和描述。
    
    当使用 - (NSDictionary *)httpParameters方法时,XLForm遵循以下规则来获取XLFormRowDescriptor值:
    1. 如果对象是NSString,NSNumber或NSDate,则该值是对象本身。
    2. 如果对象符合协议XLFormOptionObject,则XLForm将从formValue方法获取值。
    3. 否则返回nil。
    
    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
    ```
    
    一旦创建了自定义cell,你需要添加这个行定义到`cellClassesForRowDescriptorTypes`字典中,使`XLForm`知道。
    ```
    [[XLFormViewController cellClassesForRowDescriptorTypes] setObject:[MYCustomCellClass class] forKey:kMyAppCustomCellType]
    ```
    
    或者加入我们使用xib定义`XLBaseDescriptorCell`:
    ```
    [[XLFormViewController cellClassesForRowDescriptorTypes] setObject:@"nibNameWithoutNibExtension" forKey:kMyAppCustomCellType];
    ```
    这样做,当使用到`kMyAppCustomCellType`行类型时,XLForm创建合适的cell类。
    
    ## Custom Selectors - Selector Row with a custom selector view controller
    
    基本的Selector允许用户从pushed的控制器选择一个或多个items,已经能够满足需求,但是有时我们需要更多的灵活性以获取更好的用户体验,或者做一些默认不支持的事情。
    
    假如用户需要选择一个地图坐标或者选择一个从服务器获取的值。我们怎样轻易做到?
    
    ```
    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`协议。
    
    XLForm使用`XLFormRowDescriptor`实例设置`rowDescriptor`属性。
    
    开发人员负责使用rowDescriptor值更新其视图,并将所选值设置给rowDescriptor。
    
    > 注意:viewControllerClass, viewControllerNibName or viewControllerStoryboardId属性是相互独立的,均由XLFormButtonCell 和 XLFormSelectorCell使用. 如果你创建了自定义的cell,然后你负责使用它们。
    
    **另一个例子**
    ```
    row = [XLFormRowDescriptor formRowDescriptorWithTag:kSelectorUser rowType:XLFormRowDescriptorTypeSelectorPush title:@"User"];
    row.action.viewControllerClass = [UsersTableViewController class];
    ```
    你可以在实例仓库文件夹下找到这些示例细节: [Examples/Objective-C/Examples/Selectors/CustomSelectors/](https://github.com/xmartlabs/XLForm/tree/master/Examples/Objective-C/Examples/Selectors/CustomSelectors) 和 [Examples/Objective-C/Examples/Selectors/DynamicSelector](https://github.com/xmartlabs/XLForm/tree/master/Examples/Objective-C/Examples/Selectors/DynamicSelector).
    
    ## 动态表单 - 怎样在运行时动态修改表单
    
    XLFormDescriptor所做的任何更改都将反映在XLFormViewController tableView中。这意味着当添加或删除分组或行时,XLForm将相应地对该分组或行进行动画处理。
    
    我们不必再处理NSIndexPaths或添加,删除UITableViewCell了。特定TableViewCell的NSIndexPath随着时间的变化而变化,这使得很难跟踪每个UITableViewCell的NSIndexPath。
    
    每个XLForm XLFormRowDescriptor行都有一个在其构造函数中设置的tag属性。 XLFormDescriptor还有一个特定的帮助器可以从tag中获取XLFormRowDescriptor。使用tags管理XLFormRowDescriptors要容易得多,tag应该是唯一的,并且在表视图添加修改或删除过程中不会更改。
    
    重点记住,所有的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允许您定义行之间的依赖关系,以便如果一行的值更改,另一行的行为也跟着自动更改。 例如,您可能有一个表单向用户询问他/她是否拥有宠物。 如果答案是“yes”,你可能想问问他们的名字。 因此,您可以根据其他行的值,决定此行是否可见。 
    
    当然,您也可以通过观察某些行的值并相应地删除和添加行来手动执行此操作,但是很多工作XLForm已经完成了。
    
    ** 工作原理 **
    为了使行和分组自动出现和消失,每个descriptor中都有一个属性:
    
    ```
    @property id hidden;
    ```
    
    该id对象通常是NSPredicate或包含BOOL的NSNumber。它可以使用其中任何一个或NSString设置,最终从中创建NSPredicate。为了使其正常工作,字符串必须在语法上正确。
    
    例如,当first行包含值“hide”时,您可以将下面的字符串设置给second行使其消失。
    
    ```
    second.hidden = [NSString stringWithFormat:@“$%@ contains [c]'hide'”,first];
    ```
    
    这将在'$'之后插入first的tag,当然也可以手工执行。当判断predicate时,每个tag变量都将被相应的 row descriptor替代。
    
    当参数是NSString时,'.value'将附加到每个标签中,除非标签后跟'.isHidden'或'.isDisabled'。这意味着行(或分组)可能取决于另一行的值或hidden或disabled属性。当属性直接设置为NSPredicate时,它的formatString不会被更改(因此如果要引用其值,则必须在每个变量之后附加一个'.value')。设置NSString是最简单的方法,但是一些复杂的predicates可能不起作用,因此您应该直接设置NSPredicate。
    
    您还可以使用bool对象设置此属性,这意味着除非手动设置,否则属性的值不会更改。
    
    要获取评估的布尔值,应该调用isHidden方法。它不会在每次调用时重新评估predicate,只有在它依赖的行的值(或hidden/disabled状态)更改时。当这种情况发生并且返回值发生变化时,它会自动表单上反映其更改,因此不能调用其他方法。
    
    ## 禁用行(设置为只读模式)
    可以禁用行,使用户无法修改它们。 默认情况下,禁用的行是灰色的。 要禁用行,唯一要做的是设置其diabled属性:
    ```
    @property id disabled;
    ```
    此属性是包含BOOL的NSNumber、NSString或NSPredicate。 布尔将静态禁用(或启用该行)。 另外两个工作就像上面介绍的hidden属性一样。 这意味着可以根据其他行的值禁用和启用当前航。 当设置了NSString时,将生成一个NSPredicate,将该字符串作为格式化字符串,以便和目标保持一致。
    
    与hidden属性的区别在于检查行的disabled状态不会自动影响表单上的值。 因此,应该调用XLFormViewController的updateFormRow方法。
    
    ## 验证
    
    我们可以使用XLForm的验证支持验证表单数据。
    
    每一个`XLFormRowDescriptor`实例都包含了一组验证器(validators)。可以使用以下方法添加、移除验证器并验证指定行:
    
    ```
    -(void)addValidator:(id<XLFormValidatorProtocol>)validator;
    -(void)removeValidator:(id<XLFormValidatorProtocol>)validator;
    -(XLFormValidationStatus *)doValidation;
    ```
    只要定义一个对象遵循`XLFormValidatorProtocol`就能够自定义验证器:
    ```
    @protocol XLFormValidatorProtocol <NSObject>
    
    @required
    
    -(XLFormValidationStatus *)isValid:(XLFormRowDescriptor *)row;
    
    @end
    ```
    [XLFormRegexValidator](https://github.com/xmartlabs/XLForm/blob/master/XLForm/XL/Validation/XLFormRegexValidator.h)是我们能够自定义验证器的示例。
    
    一个常见的验证是值是否为空,为nil。
    
    XLFom公开所需的XLFormRowDescriptor属性以指定所需的行。
    
    要获取所有行验证错误,我们可以调用以下XLFormViewController方法:
    
    ```
    -(NSArray *)formValidationErrors;
    ```
    
    ## 额外的行配置
    
    XLFormRowDescriptor允许我们配置UITableViewCell的通用部分,例如:rowType,label,value(默认值),如果单元格`required`, `hidden` 或者`disabled`,等等。
    
    您可能需要设置`UITableViewCell`的另一个属性。要设置另一个属性XLForm使用键值编码,允许开发人员通过keyPath设置单元格属性。
    
    您只需要将属性添加到`XLFormRowDescriptor`的`cellConfig`或`cellConfigAtConfigure`的字典属性中即可。 `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];
    ```
    让我们看看如何改变单元格标签的颜色:
    ```
    row = [XLFormRowDescriptor formRowDescriptorWithTag:@"title" rowType:XLFormRowDescriptorTypeText];
    [row.cellConfig setObject:[UIColor redColor] forKey:@"textLabel.textColor"];
    [section addFormRow:row];
    ```

    相关文章

      网友评论

      • 烈霸南绝:怎么加载默认字典的值填充表单?内容过长一行显示不全该怎么动态适应高度?
      • 云画的跃光:你好,我想请教下,当rowType为XLFormRowDescriptorTypeButton, 我怎么得到button 的title的值,使用[self formValues]; 这个获取不到
      • 云画的跃光:你好,我获取的一个值是 <XLFormOptionsObject: 0x600000224680>,请问怎么得到这其中的值?
        云画的跃光:@安安静静的做一个美女子 好像得到的是索引耶,怎么得到值呢?
        云画的跃光:@安安静静的做一个美女子 kBuildConstruction 这个是什么?
        向日葵的夏天_summer:[(XLFormOptionsObject *)self.formValues[kBuildConstruction] formValue]
      • 草色初阳:大神,请教下,row之间的横线怎么去掉,还是section头部的高度可以设置为0或者不显示吗?
        草色初阳:@Mob_Developer 👌谢谢
        草色初阳:@Mob_Developer 已经解决了,谢谢
        Mob_Developer:说的是TableView的分割线吗?UITableView.seperatorStyle = UITableViewSeperatorStyleNone;
      • 群星盛宴:楼主 我遇到两个问题:1,section怎么设置高度或者不显示,只显示row。2,自定义cell时,如果有表里面还有选择器类型cell,那么点击选择器会调到自定义cell里面的update方法。 请教这个怎么搞?
        祖国的栋梁:人家README里面有说明的, 调节section的高度
        #### How to customize the header and/or footer of a section

        For this you should use the UITableViewDelegate methods in your XLFormViewController.
        This means you should implement one or both of these:

        ```objc
        -(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section

        -(UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
        ```

        Also you might want to implement the following methods to specify the height for these views:

        ```objc
        -(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section

        -(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
        ```

      本文标题:iOS复杂UITableViewCell表单实现 - XLFor

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