Neoform 1.3.0

Neoform 1.3.0

测试测试过
Lang语言 SwiftSwift
许可证 Apache 2
发布上次发布2019 年 3 月
SPM支持 SPM

Edison Santiago 维护。



Neoform 1.3.0

  • Neomode

Neoform

Neoform 是一个简单的框架,可以简化表单验证过程,而不会干扰您的 UI。

功能

  • 字段验证和掩码
  • 字段的预定义和自定义格式。
  • 表单层次结构,具有自动参数化(字典)
  • 使用返回按钮进行表单“导航”
  • 支持日期选择器、普通选择器、可搜索选择器、复选框和单选按钮
  • 支持 Storyboard
  • 无需更改 UI

安装

CocoaPods

Neoform 通过 CocoaPods 提供。只需将 pod 'Neoform' 添加到您的 Podfile 中,然后运行 pod install

入门

创建表单

创建表单就像创建一个名为的 Neoform 对象、并为它提供一个符合 NeoformElement 协议的数组对象一样简单。

我们提供了一些默认的 NeoformElement,您可以在表单中使用,下面将详细介绍(NeoformTextFieldNeoformTextViewNeoformCheckElement/NeoformCheckElementCollectionNeoformTextFieldPasswordCollection)。

Neoform 本身(它是 NeoformElementCollection 的别名词)符合 NeoformElement 协议,如以下 嵌套表单 部分中所述。

let form = Neoform(
    name: "form",
    elements: [
        self.nameField,
        self.emailField,
        self.birthDateField
    ]
)

设置格式并验证

每个 NeoformTextField 必须有一个名称和一个格式 一个选择器组,这将用于验证表单,并为您生成 JSON 类型的 Dictionary。一个像上面的简单表单的完整代码如下:

self.nameField.name = "name"
self.nameField.format = NeoformTextField.Formats.lettersOnly

self.emailField.name = "email"
self.emailField.format = NeoformTextField.Formats.email

self.birthDateField.name = "birthDate"
self.birthDateField.datePickerMaximumDate = Date()
self.birthDateField.dateFormatShow = "dd/MM/yyyy"

let form = Neoform(
    name: "form",
    elements: [
        self.nameField,
        self.emailField,
        self.birthDateField
    ]
)

要验证表单,只需调用 form.validate() throws

//do/catch ommited here for simplicity
//As explained below, you SHOULD use a try/catch here to get the errors thrown on form validation
let params = try! form.validate()
print(params)

/*
print(params) will print this:
[
    "name": "Your Name",
    "email": "[email protected]",
    "birthDate": "05/12/2017"
]
*/

通过 Storyboard 设置值

为了简化代码,NeoformTextField 的 nameformat 也可以通过 Storyboard 通过 Xcode 属性检查器的 NameFieldFormatIB 字段进行设置。由于当前 @IBInspectable 对枚举没有支持,您必须提供一个包含格式名称的 String。这仅适用于默认格式,所有支持格式列表见本文件的 NeoformTextFieldFormats 部分。

嵌套表单

如果您需要更“结构化”的 JSON,还可以嵌套表单以创建层次结构

//Field configuration omitted here for simplicity
let addressForm = Neoform(
    name: "address",
    elements: [
        self.cityField,
        self.countryField
    ]
)

let form = Neoform(
    name: "form",
    elements: [
        self.nameField,
        self.emailField,
        self.birthDateField,
        addressForm
    ]
)

let params = try! form.validate()
print(params)

/*
print(params) will print this:
[
    "name": "Your Name",
    "email": "[email protected]",
    "birthDate": "05/12/2017",
    "address": [
        "city": "Curitiba",
        "country": "Brazil""
    ]
]
*/

验证表单

表单验证很困难(这也是创建此框架的主要原因),并没有“正确的”方式来完成它。这就是为什么我们提供了三种不同的方式来验证表单

1) form.validate() throws

该方法使用 do/catch 结构,如果字段值无效,将抛出错误。字段将按 elements 数组中设置的顺序遍历,验证将在第一个无效字段处停止。

let form = Neoform(
    name: "form",
    elements: [
        self.nameField,
        self.emailField,
        self.birthDateField
    ]
)

do {
    let params = try form.validate()
}
catch {
    let formError = error as! NeoformError
    print(formError)
}

2) 错误按钮

在本验证中,将在字段的右侧显示一个图标。当点击时,将出现一个弹出窗口,包含错误信息。请注意,目前这仅适用于NeoformTextField

要激活此行为,必须在表单上将标志showErrorButton设置为true,并且包含字段的UIViewController必须符合UIPopoverControllerDelegate
form.validate() throws的行为与上述描述相同,只是会抛出错误。

let form = Neoform(
    name: "form",
    elements: [
        self.nameField,
        self.emailField,
        self.birthDateField
    ]
)

form.showErrorButton = true

do {
    let params = try form.validate()
}
catch {
    let formError = error as! NeoformError
    print(formError)
}

可以通过下述属性在每一个NeoformTextField上配置错误按钮。所有属性都有默认值,所以它可以直接使用,同时允许进一步的定制。此外,所有属性也都可以通过Interface Builder的属性检查器访问。

  • errorButtonIconImage:按钮上显示的图像。按钮有一个固定的尺寸,所以尺寸不应该有问题。默认值为信息符号。

  • errorButtonIconTintColor:应用在图像上的tintColor。在区分“大”错误和简单警告时可能很有用。默认值为UIColor.red

  • errorButtonIconSize:要显示的按钮的CGSize。默认为25x25。

  • errorMessage:在弹出窗口中显示的消息。显示前会通过:NSLocalizedString进行本地化,因此您可以提供本地化宏。默认为一个非常通用的消息。

  • centerErrorButtonWithSuperview:在某些表单设计中,我们在有边框或整个字段周围效果的情况下,将NeoformTextFieldUILabel包裹在视图中。这样,将按钮相对于字段的中心对齐看起来会很奇怪。当此标志被设置时,按钮将以垂直方式相对于NeoformTextField的父视图对齐。默认为false,错误按钮将相对于NeoformTextField垂直居中。

3) form.validateAll(onError: ((NeoformError) -> Void))

onError块将针对表单上找到的每个错误调用,这对于非常大的表单非常有用,在这种情况下,您只想警告用户一次他们犯的错误。

此方法返回的Dictionary<String,Any>只包含从表单中获取的有效信息。

NeoformTextField

NeoformTextField是表单中的主要元素和最可配置的元素。一个NeoformTextField可以有一个Format或一个Picker,尽管代码today允许您在同一个字段上设置两者,但这不受支持,并且可能导致输入和验证的问题。

如您所推测,我们使用UITextFieldDelegate来控制许多NeoformTextField的行为。如果您需要,可以通过相同的.delegate属性为字段设置自定义的UITextFieldDelegate,但不建议使用它来自定义字段的值,因为它可能引发框架冲突。请注意,具有格式设置的字段的代理方法textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Boolnever被调用,因为在这种情况下,无法保证掩码可以使用。

由于掩码的存在,不允许在文本的中间或开始处键入,也不允许粘贴。

为TextField设置初始值

有时TextField必须有一个默认值。如果您使用选择器或自定义格式,您必须通过将字符串设置到NeoformTextField.formattedText来设置字段的值。如果NeoformTextField有掩码,设置的值将在显示给用户之前按相应格式设置。如果字段具有日期选择器或选择器,这里设置的值也将选中。

//The phoneField has the mask `phoneBr` set
self.phoneField.formattedText = "4133333333"

//The value displayed on the phoneField will be "(41) 3333-3333".


self.birthDateField.dateFormatShow = "dd/MM/yyyy"
self.birthDateField.dateFormatSave = "yyyy-MM-dd"

self.birthDateField.formattedText = "2017-12-06"

//The value displayed on the birthDateField will be "06/12/2017".
//When the user taps on the field to edit the value this date will already be set on the datePicker.

NeoformTextFieldFormats

NeoformTextField的格式不仅控制字段的可能有效值,还包括用户可以输入的有效字符、字段的键盘及其掩码。我们提供了许多用于各种用途的默认格式(您也可以在FieldFormatIB属性中手动输入名称通过属性检查器来设置),但您也可以根据以下说明创建自己的格式。

当前字段的默认格式如下:

  • 可选:几乎任何字符,允许空值
  • 非空:几乎任何字符,但至少需要一个字符
  • 电子邮箱:由NSDataDetector验证,不涉及正则表达式。如果在该字段中输入了多个电子邮箱,只有最后一个将被使用。
  • 可选数字:将键盘更改为数字键盘,允许空值。
  • 仅数字:将键盘更改为数字键盘,至少需要一个字符。
  • PIN4:仅数字,将键盘更改为数字键盘,确切需要4个字符。
  • PIN6:仅数字,将键盘更改为数字键盘,确切需要6个字符。
  • 仅字母:空格也允许。
  • 货币:始终有有效值并始终显示小数部分。从0.00开始。截至目前,货币符号始终显示在数值的左侧。
  • CPF:提供巴西身份证号码的掩码。不验证验证位。
  • CEP:提供巴西邮政编码的掩码。
  • 电话(巴西):提供巴西电话号码的掩码。支持8位和9位数字。
  • 信用卡:提供格式良好的信用卡号码掩码,将数字分组为每组四个。
  • CVV:信用卡CVV,仅数字且恰好需要3个字符。
  • 密码:几乎所有字符都允许,至少6个字符。

自定义TextField格式

您可以通过创建一个采用NeoformTextFieldFormat协议的结构来创建自己的格式。请注意,截至目前,自定义格式只能通过代码设置。

简单格式

任何采用NeoformTextFieldFormat的对象必须实现以下四个属性

  • minCharactersAllowed:输入的最小有效大小。如果字段可以为空,请将其设置为0。
  • maxCharactersAllowed:输入的最大有效大小。可以设置为Int.max。
  • allowedCharactersSet:用户可以在字段中键入的字符。其他字符可以通过代码设置到字段中(例如,在电话掩码中,您可以将此设置为CharacterSet.decimalDigits并在掩码中添加诸如(, ), -等字符)。
  • keyboardType:iOS允许的任何键盘。

请注意,如果您使用自定义格式/掩码,则minCharactersAllowedmaxCharactersAllowed必须反映在应用掩码后的输入大小(即在电话掩码中,任何插入的如(, ), -的字符必须包含在这些常量中)。

高级格式化

NeoformTextFieldFormat有三个具有默认实现的方法,您可以通过重写它们来获得更多对格式化的控制。如果上述四个属性足以控制输入,则无需重写这些方法。

  • format(_ string: String) -> String?:接收任何大小和字符的字符串,并必须返回字段将显示的字符串。此方法将在设置字段的初始值时以及用户在字段中键入或删除任何字符后调用,因此在对字符串进行格式化之前“清理”字符串可能是个好主意。默认:从字符串中删除不属于指定CharacterSet的任何字符。
  • textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool: 这是一个经典的UITextFieldDelegate方法,在字段值每次变化后被调用。默认行为是如果发现无效字符或超出范围,将拒绝改变。它将调用format(_ string: String) -> String?方法,并使用该范围将返回的字符串设置为TextField的新值。
  • getValidValue(from value: String) throws -> String: 该方法由表单验证器调用。必须在当前值中发现任何错误。返回的值是应该用作参数的值,因此如果应用了掩码,对字符串进行“清洁”可能是一个好主意。

选择器

选择器对于创建一个良好的表单至关重要,且NeoformTextField内置了对三种不同类型选择器的支持。请注意,带有选择器的字段必须不设置NeoformTextFieldFormat,且一个字段只支持一个选择器。如果你将多个选择器设置为同一个字段,则只会显示最后一个。

日期选择器

默认的iOS UIDatePicker设置为UTC,并且当前区域设置为键盘上的显示,顶部有一个包含按钮的栏,用于移动到下一个元素或完成输入。

为以下任何属性设置值将激活字段的UIDatePicker

必需属性

  • dateFormatShow: 用于向用户显示日期的DateFormatter使用的格式。
    可选属性
  • dateFormatSave: 如果API使用的日期格式与显示给用户的格式不同,你可以设置一个具体的格式供DateFormatter使用,以格式化字段的初始值和参数化选定的日期。用户看到的日期将始终遵循在dateFormatShow上设置的格式。
  • datePickerMinimumDate: 设置UIDatePicker.minimumDate属性。
  • datePickerMaximumDate: 设置UIDatePicker.maximumDate属性。

选择器视图

默认的iOS UIPickerView显示在键盘上方的位置,顶部有一个包含按钮的栏,用于移动到下一个元素或完成输入。
用于pickerView的对象必须采用NeoformSelectableItemProtocol(它只需要两个String属性,一个id和一个name),因此你可以使用现有的模型与pickerView一起使用。
请注意,当一个带有pickerView的UITextField第一次成为首位响应者时,pickerView的所有组件的第一行都将被选中。

必需属性

  • pickerViewItems: 一个包含rer$ $ NeoformSelectableItemProtocol的二维数组和pickerView的组件和行。设置此属性将启用字段的UIPickerView
    可选属性
  • pickerSelectedItemToJson: ((NeoformSelectableItemProtocol) -> String)?: 当表单参数化时,我们默认使用id作为选定的picker项的默认值。如果你想使用另一个属性作为参数化值,你可以提供此闭包,并使用它返回的值代替。
  • onPickerViewSelectedItem: (() -> Void)?: 设置在此的闭包将在用户在pickerView上选择每个项时被调用。这在“链式”表单中很有用,其中选择器的值取决于另一个(例如,状态/城市的选择)。
  • pickerViewComponentsFormat: 如果pickerView有多个组件,则必须设置此属性,以便从选定的值创建字符串以显示给用户(例如,在有两个组件的信用卡到期日期选择器中,我们可能将此格式设置为"%@/%@,这样选定的值就可以显示给用户为09/19)。

当需要时,可以从 NeoformTextField 的 UIPickerView 获取当前选定的值,使用可选的 pickerViewSelectedItem 属性。

PickerTableView

Picker Table View 是一个以模态形式覆盖表单显示的 UITableViewController,包含所有可选选项的列表和一个顶部的搜索栏。这是用于大量数据集或远程加载数据的推荐选择器。
PickerTableView 所使用的对象必须遵循 PickerTableViewElementProtocol(它要求有一个 id: String 和一个计算属性 displayableName: String)。
请注意,用户可以关闭 PickerTableView 而不选择任何元素,并且在用户选择元素时,模态控制器将自动消失。

必需属性

  • loadPickerTableViewItemsHandler: ((@escaping ([PickerTableViewElementProtocol]?) -> Void) -> Void):这个闭包用于获取可用的可选元素。在这里可以进行网络请求,因为必须将选定的值数组传递给作为参数接收的闭包。如果在未接收到列表的情况下调用接收到的闭包,则可选项将永远不会显示。
    可选属性
  • viewToPresentPickerTableView:显示模态控制器的视图。 默认值: 包含 NeoformTextField 的 viewController。
  • pickerTableViewTitle:显示在 UITableViewController 顶部的导航栏中的标题。 默认值: 显示字段的标题。
  • pickerTableViewSelectedItemToJson: ((PickerTableViewElementProtocol) -> String)?:默认情况下,在参数化表单时,我们使用 id 作为默认值。如果您想使用另一个属性作为参数化值,您可以提供此闭包,并使用它返回的值代替。
  • onPickerTableViewSelectedItem: ((PickerTableViewElementProtocol) -> Void)?:当用户选择一个元素时,在此设置的闭包将被调用。

当需要时,可以使用可选的 pickerTableViewSelectedItem 属性来检索 PickerTableView 的选定值。

NeoformTextView

一个基本的 UITextView,包含一个自动创建的占位符,其功能将类似于 UITextField 的内建占位符。
当用户在 TextView 中插入文本时,占位符将消失,如果文本视图被留下且值为空,则重新出现。

与其他元素一样,它有一个必填的 name 属性,该属性将在参数化字典中使用。

  • isOptional:设置空值是否为有效值。 默认值: true
  • placeholderText:在 textView 占位符中显示的文本。
  • placeholderTextColor:占位符文本的颜色。

NeoformCheckElement

Neoform 也支持复选框。可以将 CheckElement 添加到任何表单中,并且像 NeoformTextField 一样,有一个必填的 name 属性。

在验证时,返回的值将是一个布尔值,指示复选框是否被选中。有关选择的更多选项,请参阅下面的 NeoformCheckElementCollection

NeoformCheckElement 也支持 Storyboard 支持,因此您可以在编译前查看其最终外观。

自定义

NeoformCheckElement 上有某些属性可用于自定义复选框的外观。所有这些属性也都可以通过属性检查器访问。

  • startSelected:复选框的初始状态。 默认值: false。
  • defaultModeNeoformCheckElement有两种默认模式,分别是CheckLeftCheckRight,用于定义标签相对于复选框的位置。您可以按照以下方式自定义,但如需进行高级定制,请参考进一步定制部分。
  • selectedIcon:复选框选中时显示的图标。
  • selectedIconTintColor:应用于选中图标的颜色。默认:UIColor.Black。
  • unselectedIcon:复选框未选中时显示的图标。
  • unselectedIconTintColor:应用于未选中图标的颜色。默认:UIColor.Black。

以下属性仅在使用defaultMode时使用

  • iconSize:选中/未选中图标的CGSize。
  • labelLocalizedText:标签上显示的文本。
  • labelTextColor:标签上文本的颜色。
  • labelFontSize:标签上字体的大小。

进一步定制

如果默认模式无法满足您的需求,您可以将一个UIImageView和一个UILabel拖放到NeoformCheckElement中,它们将被自动视为复选框的“图标”和“文本”。

即使使用完全定制的复选框,您仍然可以获得选中/未选中图标的行为,以及完整的表单验证!

NeoformCheckElementCollection

如果您的表单中有多个相关联的复选框,建议使用NeoformCheckElementCollection。因为它是一个专门的NeoformElementCollection,所以您可以像创建表单一样创建它,并将其添加到现有的表单中。

let notificationForm = NeoformCheckElementCollection(
    name: "notification",
    elements: [
        self.emailCheckbox
        self.pushCheckbox
    ]
)

let form = Neoform(
    name: "form",
    elements: [
        self.nameField,
        self.emailField,
        self.birthDateField,
        notificationForm
    ]
)

NeoformCheckElementCollection包含一些属性,用于帮助管理您的复选框集合

  • isSelectionOptional:设置是否可以有未选中的复选框。默认:false。
  • isRadio:设置复选框是否应该像单选按钮一样工作(一次只能选中一个)。默认:false。
  • onElementSelected: ((NeoformCheckElement) -> Void)?:当选中元素时调用的闭包。
  • saveUnselectedValues:设置是否将未选中的值添加到参数化字典中。默认:false。仅将选中的复选框添加到参数化字典中。
let notificationForm = NeoformCheckElementCollection(
    name: "notification",
    elements: [
        self.emailCheckbox
        self.pushCheckbox
    ]
)

//Let's assume the emailCheckbox is selected and pushCheckbox is not selected

notificationForm.saveUnselectedValues = false
let params = try! notificationForm.validate()
print(params)

/*
print(params) will print
[
    "email": true
]
*/

notificationForm.saveUnselectedValues = true

let params = try! notificationForm.validate()
print(params)

/*
print(params) will print
[
    "email": true,
    "push": false
]
*/

NeoformTextFieldPasswordCollection

这是一个简单的元素,用于检查密码确认密码是否匹配。密码字段可以设置一个自定义格式,该格式将被验证。如果两个字段都有效,将对匹配的值进行额外验证。

let passwordCollection = NeoformTextFieldPasswordCollection(
    name: "password",
    passwordTextField: self.passwordField,
    confirmPasswordTextField: self.confPasswordField
)

let form = Neoform(
    name: "form",
    elements: [
        self.nameField,
        self.emailField,
        passwordCollection
    ]
)

let params = try! notificationForm.validate()
print(params)

/*
print(params) will print
[
    "name": "Name",
    "email": "Email"
    "password": "Password"
]
*/

如果字段值按照格式无效,将抛出一个NeoformError.invalidValue(textField: NeoformTextField)异常。

如果字段提供的密码不匹配,将抛出一个NeoformError.passwordNotMatch(passwordCollection: NeoformTextFieldPasswordCollection)异常,并且验证不会继续。

许可证

Neorequest遵循Apache v2许可证。有关更多信息,请参阅LICENSE文件。