当前位置: > > > Swift - SwiftForms的使用详解(第三方Form表单组件 )

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

三、自带的表单元素类型

前面我只介绍了 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
  • .urlURL 地址输入。使用 URL 键盘(keyboardType .URL),且关闭自动纠错,首字母不大写。
  • .email:邮箱地址输入。使用 Email 键盘(keyboardType .emailAddress),且关闭自动纠错,首字母不大写。
  • .asciiCapableASCII 编码输入。使用 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: "备注")

三、表单元素值改变响应 

有时我们想要在表单项的值改变(比如改变了 segmentedControlpicker 的选择项)时进行一些相应的操作,只需使用对应 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)的样式为例,效果图如下:

(2)代码如下:
//标题文字样式修改
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,使其能使用我们自定义的选择器控制器。
评论2
  • 2楼
    2017-11-24 17:27
    111

    比如那些分段选择,picker等 响应事件怎么获取

    站长回复

    我在文章中补充了相关内容(三、表单元素值改变响应 ),你可以再看下。

  • 1楼
    2017-06-27 10:50
    溜溜

    感觉很6啊 航哥

    站长回复

    多谢夸奖,欢迎常来看看,我会持续更新的。