用于在iOS上构建表单的Swift框架
Formalist是一个Swift框架,用于使用简单、声明性、可读的语法在iOS上构建表单。
目录
示例
(如图所示)的示例应用程序演示了如何使用包含的表单元素构建简单的表单。
以下代码段来自示例应用程序,用于渲染表单的第一个部分
group(configuration: groupedConfiguration, elements: [
toggle(title: "Boolean Element", value: self.booleanValue),
textView(value: self.textViewValue) {
$0.placeholder = "Text View Element"
},
singleLineFloatLabel(name: "Float Label Element", value: self.floatLabelValue),
segments(title: "Segment Element", segments: [
Segment(content: .Title("Segment 1"), value: "Segment 1"),
Segment(content: .Title("Segment 2"), value: "Segment 2")
], selectedValue: self.segmentValue),
])
请注意,上述代码示例使用了构建每个表单元素的内置便利函数,这些函数是标准类初始化器的语法糖,用于提高可读性。
安装
除了手动将框架集成到您的 Xcode 项目中之外,以下包管理器得到支持
CocoaPods
将以下行添加到您的 Podfile
pod 'Formalist', '~> 0.1.0', :submodules => true
Carthage
将以下行添加到您的 Cartfile
github "seedco/Formalist" ~> 0.1.0
文档
FormValue
FormValue
是一种引用类型,它封装表单值并允许观察这些值。在创建一个带初始值的实例后,消费者代码无法直接更改该值。之后,FormValue
实例会被传到表单元素的初始化器中(如下一节所述),那些表单元素会将 UI 控件绑定到 FormValue
实例,从而使用这些控件进行用户操作将导致底层值的变化。
框架消费者可以维持一个对 FormValue
实例的引用,通过 value
属性在任意时间访问其值。消费者还可以通过使用 FormValue.addObserver(_:)
函数附加基于块的观察者来实现对值的 KVO(键值观察)样式观察。
FormElement
FormElement
协议,正如其名所示,是所有表单元素必须实现的一个协议。它仅包含一个签名为 func render() -> UIView
的方法,用于渲染该表单元素的视图。
FormElement
的一个典型实现执行以下操作
- 初始化器
- 接受一个或多个
FormValue
参数,这些参数绑定到 UI 控件 - 可选地接受额外的配置参数。这些参数通常与行为相关而不是外观,因为外观相关的属性可以直接在视图中设置,使用可选的视图配置块。与行为相关的参数示例是
TextFieldElement
的continuous
参数,它决定了表单值是否在文本编辑时连续更新。 - 可选地接受一个视图配置块,用于对元素视图进行额外的自定义。这应该是初始化器中的 最后一个参数,以便可以使用尾随闭包语法。
- 接受一个或多个
render()
函数- 创建元素视图
- 配置元素视图上的任何默认属性
- 设置目标动作或另一个回调机制,以便在值发生变化时通知,以更新相应的
FormValue
实例 - 最后一步,作为附加视图自定义,调用可选的视图配置块
- 返回视图
响应者链
该框架通过实现类似于响应者链的构造来支持使用链式表单元素视图。目前,这被用来实现内置的标签导航行为,通过在键盘上按下“回车”键在表单内的文本字段之间切换。
自定义表单元素可以使用以下两个步骤来支持这种行为
- 确保从
render()
函数返回的视图返回true
对于canBecomeFirstResponder()
- 当视图需要将焦点转移到支持第一个响应者的下一个表单视图时,应该通过框架在
UIView
扩展中添加的nextFormResponder
属性访问下一个响应者,并在其上调用becomeFirstResponder()
。例如,在TextFieldElement
中,这是通过调用委托方法来指示返回键被按下而产生的。
ValidationRule
ValidationRule
用于封装特定类型值的验证逻辑。值得注意的是,验证规则执行异步操作——这意味着,例如,您可以有一个验证规则,通过发起网络请求来验证表单值。
ValidationRule
使用一个参数为值和完成处理程序的块进行初始化。该块包含执行验证并将验证结果调用的逻辑(Valid
、Invalid(message: String)
或Cancelled
之一)。失败的验证将导致验证失败消息在导致失败表单元素下方显示给用户。
在ValidationRule
上的静态计算变量定义了几个常见情况(如必填字段和电子邮件地址)的内建规则,而ValidationRule.fromRegex()
提供了一个简单地通过正则表达式创建验证规则的途径。
验证规则传递到支持它们的表单元素的构造函数中,如TextFieldElement
。
Validatable
Validatable
是表单元素必须实现以支持其值验证的协议。它包含一个带有签名的方法 func validate(completionHandler: ValidationResult -> Void)
,该方法应实现为执行验证并调用完成处理程序以返回验证结果。此方法的常见实现是简单地调用 ValidationRule.validateRules
,它使用给定的规则数组验证值。
GroupElement
GroupElement
是表单元素层次结构中的主要非叶节点类型,它实现了一系列与渲染、布局、验证以及一组表单元素之间交互相关的重要行为。
它实现两种样式:Plain
,不渲染背景颜色和分隔符的组;以及Grouped
,默认使用特定的背景颜色和分隔符渲染组。
传递给初始化器的 GroupElement.Configuration
对象可以用于调整组的显示和行为的各个方面,包括样式、布局、分隔符视图和验证错误视图。
示例
使用固定高度的表单元素
var configuration = GroupElement.Configuration()
configuration.layout.mode = .ConstantHeight(44)
let groupElement = GroupElement(configuration: configuration, elements: [...])
使用具有内嵌大小和填充的表单元素
var configuration = GroupElement.Configuration()
configuration.style = .Grouped(backgroundColor: .whiteColor())
configuration.layout.edgeInsets = UIEdgeInsets(top: 15, left: 15, bottom: 15, right: 15)
let groupElement = GroupElement(configuration: configuration, elements: [...])
使用自定义分隔符视图
var configuration = GroupElement.Configuration()
configuration.separatorViewFactory = { (style, isBorder) in
let separatorView = SeparatorView(axis: .Horizontal)
separatorView.separatorInset = isBorder ? 0 : 20.0
separatorView.separatorColor = .redColor()
separatorView.separatorThickness = 2.0
return separatorView
}
let groupElement = GroupElement(configuration: configuration, elements: [...])
使用自定义验证错误视图
var configuration = GroupElement.Configuration()
configuration.validationErrorViewFactory = { message in
let label = UILabel(frame: CGRectZero)
label.textColor = .redColor()
label.textAlignment = .Center
label.text = message
return label
}
let groupElement = GroupElement(configuration: configuration, elements: [...])
BooleanElement
BooleanElement
显示一个 UILabel
和一个与 Bool
值绑定的 UISwitch
。
toggle(title: "Boolean Element", value: self.booleanValue)
SegmentElement
SegmentElement
展示一个 UILabel
和一个与 Segment<ValueType>
值绑定的 UISegmentedControl
。ValueType
是类型参数,表示段所代表的值的类型,对于所有段必须是统一的。每个段可以有一个标题或一个图像。
segments(title: "Segment Element", segments: [
Segment(content: .Title("Segment 1"), value: "Segment 1"),
Segment(content: .Title("Segment 2"), value: "Segment 2")
], selectedValue: self.segmentValue)
SpacerElement
SpacerElement
展示一个固定高度的空 UIView
。该视图可通过所有标准 UIView
属性(如 backgroundColor
)进行配置。
spacer(height: 20.0)
SegueElement
SegueElement
展示一个具有 UILabel
和可选 UIImageView
的视图,当点击时触发可配置的操作。
StaticTextElement
StaticTextElement
使用 UILabel
展示静态的、不可编辑的文本。
staticText("Welcome to the Forms Catalog app. This text is an example of a StaticTextElement. Other kinds of elements are showcased below.") {
$0.textAlignment = .Center
$0.font = UIFont.preferredFontForTextStyle(UIFontTextStyleFootnote)
}
EditableTextElement
EditableTextElement
是一个与 String
值绑定的元素,能够渲染多种不同的可编辑文本控件。虽然可以通过直接实例化 EditableTextElement
来创建这些元素,但使用下面代码片段中显示的便利函数要简单得多。
此元素支持通过使用 TextEditorConfiguration
结构指定的多种配置选项,包括自定义返回键行为(除了在字段之间的标准制表符行为之外),以及 String
值的验证。
λαχνός
文本字段显示一个用于一行可编辑文本的 UITextField
。
textField(value: self.emailValue, configuration: TextEditorConfiguration(continuouslyUpdatesValue: true), validationRules: [.email]) {
$0.autocapitalizationType = .None
$0.autocorrectionType = .No
$0.spellCheckingType = .No
$0.placeholder = "Text Field Element (Email)"
}
λαχνός
文本视图显示用于多行可编辑文本的 UITextView
。该文本视图实际上是 PlaceholderTextView
的一个实例,它是 UITextView
的子类,它通过使用与 UITextField
相同的 API 来支持占位符字符串。
textView(value: self.textViewValue) {
$0.placeholder = "Text View Element"
}
λαχνός
浮动标签实现了iOS原生版本的浮动标签模式。这个概念在文本字段未输入文本时也能保持标签的上下文,与传统占位符不同。
浮动标签有两种形式:单行(使用 singleLineFloatLabel
函数实例化)和多行(使用 multiLineFloatLabel
函数实例化)。单行版本使用 UITextField
作为其底层的编辑视图,多行版本使用 PlaceholderTextView
(UITextView子类
)。在两种情况下,底层的编辑视图都可以通过在可选视图配置器块内部使用 FloatLabel.textEntryView
属性来访问。
singleLineFloatLabel(name: "Float Label Element", value: self.floatLabelValue)
λαχνόςViewElement
ViewElement
提供了一种简单的方法来包装现有的视图,以便创建一个一次性自定义表单元素而不需要任何子类。它用于在示例应用程序中实现活动指示器元素。它必须与一个 FormValue
实例一起初始化,这个实例随后传入创建自定义视图的块。然而,如果视图未绑定到值(例如活动指示器),则可以简单地传递一个占位符值并在块内部忽略它。
customView(value: FormValue("")) { _ in
let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .Gray)
activityIndicator.startAnimating()
return activityIndicator
}
FormViewController
FormViewController
是 UIViewController
的子类,可以独立使用或在父视图控制器中使用 视图控制器包含 来显示表单内容。
let formViewController = FormViewController(elements: [
singleLineFloatLabel(name: "Name", value: self.nameValue),
singleLineFloatLabel(name: "Email", value: self.emailValue, validationRules: [.email]),
singleLineFloatLabel(name: "Password", value: self.passwordValue) {
$0.textEntryView.secureTextEntry = true
}
])
addChildViewController(formViewController)
view.addSubview(formViewController.view)
formViewController.view.activateSuperviewHuggingConstraints()
formViewController.didMoveToParentViewController(self)
测试
该框架通过 FBSnapshotTestCase 的快照测试和 Xcode 中的自动化 UI 测试进行了测试。
许可
本项目采用 MIT 许可证。有关更多详细信息,请参阅 LICENSE.md
。
鸣谢
Formalist 使用了 StackViewController 和 FBSnapshotTestCase。