Swift - SwiftForms的使用详解(第三方Form表单组件 )
一、基本介绍
1,什么是SwiftForms?
SwiftForms 是一个强大且灵活的第三方 Swift 组件库。它可以轻松地通过几行代码创建复杂表单,同时也能很方便地定制单元格显示样式。
2,安装配置
(1)从 GitHub 上下载最新的代码:https://github.com/ortuman/SwiftForms
(2)将下载下来的源码包中 SwiftFormsApplication.xcodeproj 拖拽至你的工程中

(3)工程 -> General -> Embedded Binaries 项,把 SwiftForms.framework 添加进来。

(4)最后,在需要使用 SwiftForms 的地方 import 进来就可以了。
import SwiftForms
二、使用说明
SwiftForms 使用起来十分简单,只需让视图控制器继承 FormViewController。然后定义一个 FormDescriptor 实例,最后再定义 FormDescriptor 相关的 section 及其 rows 即可。1,效果图
(1)这里实现一个最简单的登录表单,一共两个分区(Section):
- 第一个分区有两个表单项,分别可以输入普通文字和密码。
- 第二个分区只有一个按钮。

(2)点击登录按钮,如果当前表单正在编辑输入则停止编辑。同时获取最新的表单值,并打印出来。

2,实现步骤
(1)首先继承 FormViewController 创建一个视图控制器(MyFormViewController),其内容如下:
import UIKit
import SwiftForms
class MyFormViewController: FormViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "hangge.com"
//创建form实例
let form = FormDescriptor()
form.title = "Example form"
//第一个section分区
let section1 = FormSectionDescriptor(headerTitle: nil, footerTitle: nil)
//用户名
var row = FormRowDescriptor(tag: "name", type: .text, title: "用户名")
row.configuration.cell.appearance =
["textField.placeholder" : "手机号/邮箱号" as AnyObject,
"textField.textAlignment" : NSTextAlignment.right.rawValue as AnyObject]
section1.rows.append(row)
//密码
row = FormRowDescriptor(tag: "pass", type: .password, title: "密码")
row.configuration.cell.appearance["textField.textAlignment"]
= NSTextAlignment.right.rawValue as AnyObject
section1.rows.append(row)
//第二个section分区
let section2 = FormSectionDescriptor(headerTitle: nil, footerTitle: nil)
//提交按钮
row = FormRowDescriptor(tag: "button", type: .button, title: "登录")
row.configuration.button.didSelectClosure = { _ in
self.submit()
}
section2.rows.append(row)
//将两个分区添加到form中
form.sections = [section1, section2]
self.form = form
}
//登录按钮点击
func submit() {
//取消当前编辑状态
self.view.endEditing(true)
//将表单中输入的内容打印出来
let message = self.form.formValues().description
print(message)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
(2)打开 Main.storyboard,删除默认的 View Controller。拖入一个 Table View Controller,并勾选“Is Initial View Controller”

(3)由于页面有多个分区,这里将 tableView 的 style 设置成 Grouped 会更加好看些。

(4)最后将这个 Table View Controller 的 Custom Class 设置为我们前面定义的 MyFormViewController 即可。

源码下载:
hangge_1720.zip

(2)可以设置默认值,以及输入框文字靠右显示。
三、自带的表单元素类型
前面我只介绍了 3 种简单的表单项,SwiftForms 其实提供了许多常用的表单组件(分别对应不同的 type)供我们使用,下面分别进行介绍。
1,普通的文本标签(label)
var row = FormRowDescriptor(tag: "test", type: .label, title: "这时一个文本标签单元格") row.configuration.cell.placeholder = "右侧文字"
2,普通的文本输入(text)
(1)输入时,这个使用的是最普通的虚拟键盘。即 textField.keyboardType = .default

var row = FormRowDescriptor(tag: "test", type: .text, title: "用户名") row.configuration.cell.placeholder = "你的工号"
(2)可以设置默认值,以及输入框文字靠右显示。
row.configuration.cell.appearance["textField.textAlignment"]
= NSTextAlignment.right.rawValue as AnyObject
row.value = "A007" as AnyObject
3,特殊的文本输入
根据类型的不同,会自动使用相应的虚拟键盘。
- .number:数字输入。使用数字键盘(keyboardType 为 .numberPad)
- .decimal:带小数的数字输入。使用带小数点的数字键盘(keyboardType 为 .decimalPad)
- .numbersAndPunctuation:数字和标点符号输入。使用带数字和标点的键盘(keyboardType 为 .numbersAndPunctuation)
- .name:名字输入。使用普通键盘(keyboardType 为 .default),关闭自动纠错,同时单词首字母大写。
- .phone:电话输入。使用电话键盘(keyboardType 为 .phonePad)
- .url:URL 地址输入。使用 URL 键盘(keyboardType 为 .URL),且关闭自动纠错,首字母不大写。
- .email:邮箱地址输入。使用 Email 键盘(keyboardType 为 .emailAddress),且关闭自动纠错,首字母不大写。
- .asciiCapable:ASCII 编码输入。使用 ASCII 键盘(keyboardType 为 .asciiCapable),且关闭自动纠错,首字母不大写。
- .password:密码输入。使用普通键盘名,不过 textField 使用密文显示,且再次编辑自动清空原先里面的内容。

var row = FormRowDescriptor(tag: "test", type: .decimal, title: "本月消费")
4,按钮(button)

var row = FormRowDescriptor(tag: "test", type: .button, title: "提交")
row.configuration.button.didSelectClosure = { _ in
//取消当前编辑状态
self.view.endEditing(true)
}
5,开关(booleanSwitch)

let row1 = FormRowDescriptor(tag: "test1", type: .booleanSwitch, title: "消息通知") row1.value = true as AnyObject let row2 = FormRowDescriptor(tag: "test2", type: .booleanSwitch, title: "自动回复")
6,勾选(booleanCheck)

let row1 = FormRowDescriptor(tag: "test1", type: .booleanCheck, title: "电影") let row2 = FormRowDescriptor(tag: "test2", type: .booleanCheck, title: "音乐") row2.value = true as AnyObject
7,分段选择(segmentedControl)

//创建分段选择单元
var row = FormRowDescriptor(tag: "test1", type: .segmentedControl, title: "测试等级")
//设置分段值
row.configuration.selection.options = [1,2,3] as [AnyObject]
//设置分段值对应的文字
row.configuration.selection.optionTitleClosure = { value in
guard let option = value as? Int else { return "" }
switch option {
case 1: return "初级"
case 2: return "中级"
case 3: return "高级"
default: return "" }
}
//设置默认值
row.value = 2 as AnyObject
//设置了默认值,分段选择控件的选中项也要同步修改
row.configuration.cell.appearance["segmentedControl.selectedSegmentIndex"] = 1
8,选择框(picker)

//创建选择框单元
var row = FormRowDescriptor(tag: "test1", type: .picker, title: "测试等级")
row.configuration.cell.showsInputToolbar = true
//设置选项值
row.configuration.selection.options = [1,2,3] as [AnyObject]
//设置选项值值对应的文字
row.configuration.selection.optionTitleClosure = { value in
guard let option = value as? Int else { return "" }
switch option {
case 1: return "初级"
case 2: return "中级"
case 3: return "高级"
default: return "" }
}
//设置默认值
row.value = 2 as AnyObject
9,日期时间选择(date、time、dateAndTime)
(1)只有日期(date)

var row = FormRowDescriptor(tag: "birthday", type: .date, title: "生日")
(2)只有时间(time)

var row = FormRowDescriptor(tag: "birthday", type: .time, title: "每日更新时间")
(3)既有日期又有时间(dateAndTime)
var row = FormRowDescriptor(tag: "birthday", type: .dateAndTime, title: "结束")
10,步进器(stepper)
var row = FormRowDescriptor(tag: "count", type: .stepper, title: "计数") //允许的最大值 row.configuration.stepper.maximumValue = 200.0 //允许的最小值 row.configuration.stepper.minimumValue = 20.0 //每次增减的值 row.configuration.stepper.steps = 2.0
11,滑块(slider)
var row = FormRowDescriptor(tag: "count", type: .slider, title: "背景音乐") //允许的最大值 row.configuration.stepper.maximumValue = 200.0 //允许的最小值 row.configuration.stepper.minimumValue = 0.0
12,多项选择(multipleSelector)
点击单元格后会跳转新页面,列出所有的选项进行多选。

var row = FormRowDescriptor(tag: "interest", type: .multipleSelector, title: "兴趣爱好")
row.configuration.selection.options = ([1, 2, 3, 4] as [Int]) as [AnyObject]
row.configuration.selection.allowsMultipleSelection = true
row.configuration.selection.optionTitleClosure = { value in
guard let option = value as? Int else { return "" }
switch option {
case 1:
return "听音乐"
case 2:
return "看电影"
case 3:
return "玩游戏"
case 4:
return "读书"
default:
return ""
}
}
13,多行文本输入(multilineText)

var row = FormRowDescriptor(tag: "text", type: .multilineText, title: "备注")
三、表单元素值改变响应
有时我们想要在表单项的值改变(比如改变了 segmentedControl、picker 的选择项)时进行一些相应的操作,只需使用对应 cell 的 didUpdateClosure 回调方法即可。
1,效果图
这里我们使用步进器(stepper)为例。当点击加号、减号改变 stepper 的值时,控制台会把当前的值打印出来。

2,样例代码
(高亮部分表示对应的响应回调)
var row = FormRowDescriptor(tag: "count", type: .stepper, title: "计数")
//允许的最大值
row.configuration.stepper.maximumValue = 200.0
//允许的最小值
row.configuration.stepper.minimumValue = 20.0
//每次增减的值
row.configuration.stepper.steps = 2.0
//该表单元素值改变时的响应
row.configuration.cell.didUpdateClosure = { row in
print("--- 值发生改变 ---")
print("type:\(row.type)")
print("tag:\(row.tag)")
print("title:\(row.title!)")
print("value:\(row.value!)")
}
四、外观样式的修改
1,设置样式
表单中每一行描述都有一个配置字典(row.configuration.cell.appearance),通过它我们可以定制单元格的外观和行为。(1)这里以设置分段选择(segmentedControl)的样式为例,效果图如下:

//标题文字样式修改 row.configuration.cell.appearance["titleLabel.font"] = UIFont.boldSystemFont(ofSize: 30.0) //分段选择控件颜色修改 row.configuration.cell.appearance["segmentedControl.tintColor"] = UIColor.red
2,单元格头部添加图标
这个同普通的 tableView 一样,只要在 cellForRowAt 方法中给需要的 cell 设置图片即可。
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
-> UITableViewCell {
//获取默认单元格
let cell = super.tableView(tableView, cellForRowAt: indexPath)
//如果是第一个单元格则添加邮件图标
if indexPath.section == 0 && indexPath.row == 0 {
cell.imageView?.image = UIImage(named: "mail.png")
}
return cell
}
五、创建自定义单元格
虽然 SwiftForms 已经提供了各种类型的单元格供我们使用,但有时可能还不能满足需求。通过继承 FormBaseCell,我们可以很方便的实现我们自己的表单 cell。继承后记得要实现如下两个方法:
- configure:这个只有在 cell 创建完毕后会调用一次。
- update:每次 cell 内容需要刷新的时候都会调用。
1,效果图
这里自定义一个简单的表单 cell,其内部左侧显示一个圆形的图标,右侧是文本输入框。

2,组件代码(MyImageHeadCell.swift)
import UIKit
import SwiftForms
class MyImageHeadCell: FormBaseCell {
//左侧图片视图
open let headImageView = UIImageView()
//右侧输入框
open let textField = UITextField()
//初始化配置(只会在单元格创建完毕后调用一次)
override func configure() {
super.configure()
//去除选中样式
selectionStyle = .none
//图片保持比例缩放
headImageView.contentMode = .scaleAspectFill
//设置遮罩
headImageView.layer.masksToBounds = true
//输入框文字样式
textField.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.body)
//输入框内容编辑响应时间
textField.addTarget(self, action: #selector(MyImageHeadCell.editingChanged(_:)),
for: .editingChanged)
//将组件添加到视图中
contentView.addSubview(headImageView)
contentView.addSubview(textField)
}
//每次需要更新单元格是都会调用
override func update() {
super.update()
//设置输入框文字
textField.text = rowDescriptor?.value as? String
textField.placeholder = rowDescriptor?.configuration.cell.placeholder
textField.isSecureTextEntry = false
textField.clearButtonMode = .whileEditing
}
//布局
override func layoutSubviews() {
super.layoutSubviews()
//圆形图标直径为单元格高度的3/4
let diameter = super.frame.height / 4 * 3
//设置图标的尺寸、位置
headImageView.frame.size = CGSize(width: diameter, height: diameter)
headImageView.center = CGPoint(x:diameter, y: super.frame.height / 2)
//设置圆角半径(宽度的一半),显示成圆形。
headImageView.layer.cornerRadius = diameter / 2
//设置输入框的尺寸和位置
textField.frame = CGRect(x: diameter * 2, y: 0,
width: super.frame.width - diameter * 2,
height: super.frame.height)
}
//输入框内容编辑
internal func editingChanged(_ sender: UITextField) {
guard let text = sender.text, text.characters.count > 0
else { rowDescriptor?.value = nil; update(); return }
rowDescriptor?.value = text as AnyObject
}
}
3,测试代码
import UIKit
import SwiftForms
class MyFormViewController: FormViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "hangge.com"
//创建form实例
let form = FormDescriptor()
form.title = "Example form"
//第一个section分区
let section1 = FormSectionDescriptor(headerTitle: nil, footerTitle: nil)
//第1个单元格
var row = FormRowDescriptor(tag: "windows", type: .unknown, title: "Windows")
row.configuration.cell.appearance["headImageView.image"] =
UIImage(named: "windows.png") as AnyObject
row.configuration.cell.cellClass = MyImageHeadCell.self
section1.rows.append(row)
//第2个单元格
row = FormRowDescriptor(tag: "ruby", type: .unknown, title: "Ruby")
row.configuration.cell.appearance["headImageView.image"] =
UIImage(named: "ruby.png") as AnyObject
row.configuration.cell.cellClass = MyImageHeadCell.self
section1.rows.append(row)
//第二个section分区
let section2 = FormSectionDescriptor(headerTitle: nil, footerTitle: nil)
//提交按钮
row = FormRowDescriptor(tag: "button", type: .button, title: "提交")
row.configuration.button.didSelectClosure = { _ in
self.submit()
}
section2.rows.append(row)
//将两个分区添加到form中
form.sections = [section1, section2]
self.form = form
}
//登录按钮点击
func submit() {
//取消当前编辑状态
self.view.endEditing(true)
//将表单中输入的内容打印出来
let message = self.form.formValues().description
print(message)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
六、创建自定义的选择控制器(Selector Controllers)
SwiftForms 自带了一个选择控制器 FormOptionsSelectorController,像前面提到的多项选择 cell 就用到了这个选择控制器,点击表单 cell 后就会跳转到新页面进行多选操作。
为了满足一些特殊的业务需求,可能需要实现自定义的选择控制器。我们只要遵守 FormSelector 协议,即可通过 cell 对象显示这个选择控制器,更改属性值。同时还可以根据用户的操作,改变表单行值(row value)。具体写法我就不举例了,大家可以参考 FormOptionsSelectorController.swift
注意:定义好选择控制器类之后,别忘了设置 row.configuration.selection.controllerClass,使其能使用我们自定义的选择器控制器。

比如那些分段选择,picker等 响应事件怎么获取
感觉很6啊 航哥