TLFormView 0.0.5

TLFormView 0.0.5

测试已测试
Lang语言 Obj-CObjective C
许可证 MIT
发布最后发布2015年5月

Bruno Berisso 维护。



  • 作者:
  • BrunoBerisso

TLFormView 是 又一个 真正通用的表单视图。这意味着相同的组件可以使用机制通过 Auto Layout 可视格式 来调整布局以适应运行中的设备。

因为它不扩展 UITableView,所以您可以完全自由地创建任何作为表单字段的内容,只要它扩展了基本 TLFormField 类。它还有一些很酷的功能,比如:使用 NSPredicate 进行条件可见性,每个字段都带有 UIPopoverControler 位置的辅助,以及即时编辑/只读模式切换等功能。

使用方法

表单设置

有两个基本组件:TLFormViewTLFormFieldTLFormView 继承自 UIScrollView 并添加了另一个数据源和代理以创建和处理表单事件。要使表单实用,您需要实现 TLFormViewDataSource 协议的三个方法,这些方法是

- (NSArray *)fieldNamesToShowInFormView:(TLFormView *)form;

- (TLFormField *)formView:(TLFormView *)form fieldForName:(NSString *)fieldName;

- (NSArray *)constraintsFormatForFieldsInForm:(TLFormView *)form;

fieldNamesToShowInFormView: 返回一个包含用于识别表单字段(或 id)的字符串数组的数组。 formView:fieldForName: 为表单中的每个字段名创建一个字段。字段是任何扩展 TLFormField(即扩展 UIView 的子类),有一组默认字段可以用于适合 80% 的用例。这些是

  • TLFormFieldImage:用于显示从 url 或原始图像获取的图像
  • TLFormFieldList:用于显示事物列表
  • TLFormFieldMultiLine:用于显示长文本
  • TLFormFieldSingleLine:用于显示短文本、数字和布尔值
  • TLFormFieldTitle:用于显示格式为标题的短文本

该项目在 TLFormField 上有一个类别,带有用于自定义字段外观的基本方法。它有一些方法来配置标题,并提供对内部组件的 UIAppearance 代理的访问。

一旦您为表单中的每个字段提供一个)TLFormField,就需要定义如何布局这些字段。要做到这一点,您需要实现数据源的第三个方法 constraintsFormatForFieldsInForm:,并返回一个包含放置字段在屏幕上的规则的字符串数组的数组。在编写规则时,您需要引用在 fieldNamesToShowInFormView: 实现中返回的字段名称。在这里,您有机会检查设备的功能,并根据需要更改规则来调整布局。

以下是 TLFormViewDataSource 中三个必要方法的示例实现:

- (NSArray *)fieldNamesToShowInFormView:(TLFormView *)form {
    return @[
        @"user_name",
        @"avatar",
        @"age",
    ];
}

- (TLFormField *)formView:(TLFormView *)form fieldForName:(NSString *)fieldName {

    Class fieldClass;
    NSString *title;
    id value;

    if ([fieldName isEqualToString:@"user_name"]) {
        fieldClass = [TLFormFieldSingleLine class];
        title = @"User Name";
        value = userModel.name;
    }
    else if ([fieldName isEqualToString:@"avatar"]) {
        fieldClass = [TLFormFieldImage class];
        title = @"Avatar";
        value = userModel.avatarUrl;
    }
    else {
        fieldClass = [TLFormFieldSingleLine class];
        title = @"Age";
        value = userModel.age;
    }

    return [fieldClass formFieldWithName:fieldName title:title andDefaultValue:value];
}

- (NSArray *)constraintsFormatForFieldsInForm:(TLFormView *)form {

    //For iPhone we want a vertical layout like we get on a UITableView

    if (isIPhone) {
        return @[
            //Place the avatar on the top
            @"V:|-[avatar(==230)]-",
            @"H:|-[avatar(==420)]-|",

            //Now place all the fields to the bottom
            @"V:[avatar]-[user_name(>=44)]-",
            @"H:|-[user_name]-|",

            @"V:[age(==user_name)]-|",
            @"H:|-[age]-|"
        ];

    //For anything else we will place the image on the top left and the rest of the fields to the right
    } else {
        return @[

            //Place the avatar on the top left
            @"V:|-[avatar(==230)]",
            @"H:|-[avatar]",

            //Now place all the fields to the right
            @"V:|-[user_name(>=44)]",
            @"H:|-[avatar]-[user_name]-|",

            @"V:[user_name]-[age(==user_name)]-|",
            @"H:|-[avatar(==420)]-[age]-|"
        ];
    }
}

以下是iPhone上的示例:

以下是iPad上的示例:

条件可见性

有些情况下,您需要仅当其他字段具有特定值时才显示特定字段。为此,TLFormField具有一个属性 visibilityPredicate,您可以在创建字段时将其设置为 NSPredicate。该谓词可以通过NSPredicate变量替换 访问表单中所有字段的值。这意味着您可以使用语法 $[字段名称].value 访问表单中的任何字段值。

假设我们要在“年龄”大于12岁的情况下显示“用户名”字段。我们可以编写一个谓词,当“年龄”字段大于12时返回true,并将其作为“user_name”字段的 visibilityPredicate。以下是对应的代码:

- (TLFormField *)formView:(TLFormView *)form fieldForName:(NSString *)fieldName {
    ...
    //Asume that 'field' is an instance of TLFormField created before somehow
    if ([fieldName isEqualToString:@"user_name"])
        field.visibilityPredicate = [NSPredicate predicateWithFormat:@"$age.value > 12"];
    ...
}
原地帮助

假设我们想要简明扼要地解释上一个示例中的奇怪行为。为此,我们可以使用TLFormField提供的原地支持。我们只需要在字段的helpText属性中设置帮助文本字符串。当存在该属性的值时,字段将在字段标题旁边显示一个问号图标,当用户点击该图标时,会在弹出窗口中显示帮助文本。以下是对应的代码:

- (TLFormField *)formView:(TLFormView *)form fieldForName:(NSString *)fieldName {
    ...
    if ([fieldName isEqualToString:@"age"])
        field.helpText = @"This is the age of the user. Users with less than 12 years are not allowed to enter his name.";
    ...
}

单击图标后的表现如下:

TLFormModel

为了帮助您设置表单,存在一个名为 TLFormModel 的类,它可以自动推断出从自己的分类中实现的TLFormViewDataSource。您只需要扩展它并添加一个属性,对于您想在表单中显示的每个字段,该属性的类型必须在文件 TLFormModel.h 中声明的类型之一。每个类型对应于提供的标准TLFormField之一。

要读取表单的值,就像访问任何对象一样访问属性。要写入值,您需要将值包装在为每个类型提供的值构造函数中。一旦模型中的值更新,您需要手动执行一个重新加载,使用TLFormViewreloadValues 方法来显示新的值(这是一个便宜的更新,不涉及视图销毁)。

以下是TLFormModel支持的类型声明:

  • TLFormSeparator:扩展NSObject。在表单中表现为分隔符,允许将字段分组到部分。没有值或标题。
  • TLFormText:扩展NSString。
  • TLFormLongText:扩展NSString。
  • TLFormTitle:扩展NSString。
  • TLFormNumber:扩展NSNumber。
  • TLFormBoolean:扩展NSNumber。
  • TLFormList:扩展NSArray。
  • TLFormEnumerated:扩展NSDictionary。
  • TLFormImage:扩展NSObject。

这些类除了从其超类继承的逻辑外没有其他任何逻辑,它们就像一个覆盖在属性上的注释。构建此类类型的值的方式是以在 TLFormModel.h 中声明的普通 C 函数,这些函数检查参数的类型并复制给定的参数值。

使用其属性的类型和值,TLFormModel可以推断出应该为每个属性使用哪种表单字段。对于字段标题,使用属性名称。如果属性名称使用蛇形命名约定,则每个下划线(_)转换为空格,所有单词均首字母大写,因此属性 "user_name" 将具有标题 "User Name"。字段的顺序是声明属性的顺序。例如

@interface UserModel : TLFormModel

@property (nonatomic, strong) TLFormImage *avatar;
@property (nonatomic, strong) TLFormText *user_name;

@end

将头像字段置于顶部,用户名在其下方。

我们的先前示例可以用TLFormModel这样编写

@interface UserModel : TLFormModel

@property (nonatomic, strong) TLFormImage *avatar;
@property (nonatomic, strong) TLFormText *user_name;
@property (nonatomic, strong) TLFormNumber *age;

@end

@implementation UserModel @end

就是这样。这将生成类似于我们在所有平台上在iPhone上获得的垂直布局。现在要连接TLFormModel到表单,我们需要使用我们使用的TLFormView上的setFormModel:方法。

...

//At some point in some place...
FormUserModel *formUserModel = [FormUserModel new];

//This copy the values of our user model to the form model constructing the correct types using plain C functions
formUserModel.avatar = TLFormImage(userModel.avatar);
formUserModel.user_name = TLFormText(userModel.userName);
formUserModel.age = TLFormNumber(userModel.age);

TLFormView *form = ...
[form setFormModel:formUserModel];

要为其他设备家族调整布局,我们需要重写由TLFormModel提供的constraintsFormatForFieldsInForm:的实现,如下所示

@implementation UserModel

- (NSArray *)constraintsFormatForFieldsInForm:(TLFormView *)form {

    //For iPhone we want the default implementation provided so just return the 'super' version
    if (isIPhone)
        return [super constraintsFormatForFieldsInForm:form];

    //For anything else use our custom layout
    else {
        return @[

            //Place the avatar on the top left
            @"V:|-[avatar(==230)]",
            @"H:|-[avatar]",

            //Now place all the fields to the right
            @"V:|-[user_name(>=44)]",
            @"H:|-[avatar]-[user_name]-|",

            @"V:[user_name]-[age(==user_name)]-|",
            @"H:|-[avatar(==420)]-[age]-|"
        ];
    }
}

@end

现在假设我们想编辑此用户信息并获取结果。我们需要切换表单的编辑状态并从我们使用的TLFormModel实例中读取更新的值。我们可以这样做

- (IBAction)toggleEditionAction:(id)sender {
    //Set the form on edit mode
    self.form.editing = !self.form.editing;
    //update the fields to reflect it
    [self.form setupFields];
}

- (IBAction)saveUserAction:(id)sender {
    //Read the values entered by the user and save it to disc.

    NSDictionary values = @{
                            @"avatar": formUserModel.avatar,
                            @"user_name": formUserModel.name,
                            @"age": formUserModel.age
                        }
    [values writeToURL:[self saveUrl] atomically:YES];
}

关于"编辑"图片。默认的TLFormFieldImage在此点不提供选择图片的方法。您需要处理字段选择并显示某种类型的图片选择器。

事件处理

TLFormView通过TLFormViewDelegate初出茅庐时报告两个事件:didSelectField:didChangeValueForField:。事件由通知表单的TLFormField实现触发,然后表单传播事件。根据字段的实现方式,它可能会有更多的报告事件。例如,TLFormFieldList使用UITableView来呈现值列表,因此它有自己的委托,可以自定义行是否可以重新排列或选择单行。

需求

iOS >= 8.0

安装

或克隆存储库,并在Pod/Classes下查看代码。

待办事项

有许多需要改进的地方,以下是一些

  • [ ] 使用TLFormField+UIAppearance.h中的方法集中样式
  • [ ] 将"默认度量"作为样式的一部分公开
  • [ ] TLFormModel:寻找一种更好的方式来提高对象变更的效率
  • [ ] 添加键盘下一/前一按钮
  • [ ] 使用预测验证
  • [ ] 重构 TLFormView 到 TLFormViewController 以自动处理:选择照片、向列表中添加字符串、平面布置等。

如果您想为该项目做出贡献,请考虑选择以下项之一。

作者

BrunoBerisso, [email protected]

许可

TLFormView可在MIT许可下获得。有关更多信息,请参阅LICENSE文件。