Neoform 是一个简单的框架,可以简化表单验证过程,而不会干扰您的 UI。
Neoform
通过 CocoaPods 提供。只需将 pod 'Neoform'
添加到您的 Podfile 中,然后运行 pod install
创建表单就像创建一个名为的 Neoform
对象、并为它提供一个符合 NeoformElement
协议的数组对象一样简单。
我们提供了一些默认的 NeoformElement
,您可以在表单中使用,下面将详细介绍(NeoformTextField
、NeoformTextView
、NeoformCheckElement/NeoformCheckElementCollection
和 NeoformTextFieldPasswordCollection
)。
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"
]
*/
为了简化代码,NeoformTextField 的 name
和 format
也可以通过 Storyboard 通过 Xcode 属性检查器的 Name
和 FieldFormatIB
字段进行设置。由于当前 @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""
]
]
*/
表单验证很困难(这也是创建此框架的主要原因),并没有“正确的”方式来完成它。这就是为什么我们提供了三种不同的方式来验证表单
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)
}
在本验证中,将在字段的右侧显示一个图标。当点击时,将出现一个弹出窗口,包含错误信息。请注意,目前这仅适用于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
:在某些表单设计中,我们在有边框或整个字段周围效果的情况下,将NeoformTextField
和UILabel
包裹在视图中。这样,将按钮相对于字段的中心对齐看起来会很奇怪。当此标志被设置时,按钮将以垂直方式相对于NeoformTextField
的父视图对齐。默认为false
,错误按钮将相对于NeoformTextField
垂直居中。
form.validateAll(onError: ((NeoformError) -> Void))
onError
块将针对表单上找到的每个错误调用,这对于非常大的表单非常有用,在这种情况下,您只想警告用户一次他们犯的错误。
此方法返回的Dictionary<String,Any>
只包含从表单中获取的有效信息。
NeoformTextField
是表单中的主要元素和最可配置的元素。一个NeoformTextField
可以有一个Format
或一个Picker
,尽管代码today
允许您在同一个字段上设置两者,但这不受支持,并且可能导致输入和验证的问题。
如您所推测,我们使用UITextFieldDelegate
来控制许多NeoformTextField
的行为。如果您需要,可以通过相同的.delegate
属性为字段设置自定义的UITextFieldDelegate
,但不建议使用它来自定义字段的值,因为它可能引发框架冲突。请注意,具有格式设置的字段的代理方法textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool
将never
被调用,因为在这种情况下,无法保证掩码可以使用。
由于掩码的存在,不允许在文本的中间或开始处键入,也不允许粘贴。
有时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.
NeoformTextField
的格式不仅控制字段的可能有效值,还包括用户可以输入的有效字符、字段的键盘及其掩码。我们提供了许多用于各种用途的默认格式(您也可以在FieldFormatIB
属性中手动输入名称通过属性检查器来设置),但您也可以根据以下说明创建自己的格式。
当前字段的默认格式如下:
您可以通过创建一个采用NeoformTextFieldFormat
协议的结构来创建自己的格式。请注意,截至目前,自定义格式只能通过代码设置。
任何采用NeoformTextFieldFormat
的对象必须实现以下四个属性
minCharactersAllowed
:输入的最小有效大小。如果字段可以为空,请将其设置为0。maxCharactersAllowed
:输入的最大有效大小。可以设置为Int.max。allowedCharactersSet
:用户可以在字段中键入的字符。其他字符可以通过代码设置到字段中(例如,在电话掩码中,您可以将此设置为CharacterSet.decimalDigits
并在掩码中添加诸如(, ), -
等字符)。keyboardType
:iOS允许的任何键盘。请注意,如果您使用自定义格式/掩码,则minCharactersAllowed
和maxCharactersAllowed
必须反映在应用掩码后的输入大小(即在电话掩码中,任何插入的如(, ), -
的字符必须包含在这些常量中)。
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
属性。
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 的选定值。
一个基本的 UITextView
,包含一个自动创建的占位符,其功能将类似于 UITextField
的内建占位符。
当用户在 TextView 中插入文本时,占位符将消失,如果文本视图被留下且值为空,则重新出现。
与其他元素一样,它有一个必填的 name
属性,该属性将在参数化字典中使用。
isOptional
:设置空值是否为有效值。 默认值: trueplaceholderText
:在 textView 占位符中显示的文本。placeholderTextColor
:占位符文本的颜色。Neoform 也支持复选框。可以将 CheckElement 添加到任何表单中,并且像 NeoformTextField
一样,有一个必填的 name
属性。
在验证时,返回的值将是一个布尔值,指示复选框是否被选中。有关选择的更多选项,请参阅下面的 NeoformCheckElementCollection
。
NeoformCheckElement
也支持 Storyboard 支持,因此您可以在编译前查看其最终外观。
NeoformCheckElement
上有某些属性可用于自定义复选框的外观。所有这些属性也都可以通过属性检查器访问。
startSelected
:复选框的初始状态。 默认值: false。defaultMode
:NeoformCheckElement
有两种默认模式,分别是CheckLeft
和CheckRight
,用于定义标签相对于复选框的位置。您可以按照以下方式自定义,但如需进行高级定制,请参考进一步定制
部分。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文件。