GenericUI
通用UI为您提供用于iOS项目的美观且通用的UI元素。目前它主要关注输入和表格,但还有更多功能即将推出。
内容
要求
- Swift 5.2
- iOS 9.0+
组件
目前只有几个组件可用。它们拥有超强的可定制性,应该能让您节省大量时间和代码。
输入
UIInputField<输出类型>
这是一个非常简单的泛型扩展,用于UITextField
,旨在将输入转换为泛型类型。
UIActiveInput<输出类型>
这是一个更复杂的输入,根据UIInputField<输出类型>
结合了多个视图。它非常可定制,但默认提供良好设计、标签、活动状态的动画指示器等。它公开了原始文本字段的API,包括无障碍功能。
表单
UIQuickForm<模型类型>
这是一个简单的组件,允许您构建美丽的表单,可以从用户处收集通用的模型。UIQuickForm
为您处理所有布局。
更多...
更多内容即将到来...
示例
字符串输入
下面的代码会创建一个标签为“FirstName”的输入框,允许用户输入文本内容,输出结果为 String
。
let input = UIActiveInput<String>("Firstname")
addSubview(input)
let firstname: String = input.output // returns String?
无符号整数输入
以下是一个从用户那里收集无符号整数的输入示例。输出结果为 UInt
。该 UIActiveInput
智能地自动将键盘设置为不带小数点的数字键盘。
let input = UIActiveInput<UInt>("Age")
addSubview(input)
let age: UInt = input.output // returns UInt?
T
自定义对象输入
您可以从 UIActiveInput
收集自己的模型类型,但该类型需要符合几个协议,特别是:StringTwoWayConvertible
和 BestKeyboardType
。这些协议的目的是确保您的对象提供从 String
(因为 UIActiveInput
是基于原生 UITextField
的)转换的方法,并指定最佳使用的 iOS 键盘类型。StringTwoWayConvertible
是您提供自定义字符串转换的方式。
通过扩展,您也可以使现有或第三方类型能够通过 UIActiveInput
返回。
示例
class MyCustomType : StringTwoWayConvertible, BestKeyboardType {
var number: Double
init?(_ text: String) {
// Write your own String -> Object transform
guard let n = Double(text) else {
return
}
number = n
}
var description: String {
// Write your own Object -> String transform
return "\(number)"
}
static var bestKeyboardType: UIKeyboardType {
return .decimalPad
}
}
let input = UIActiveInput<MyCustomType>()
UIActiveInput
自定义
UIActiveInput
深度可定制,直接基于原生 UITextField
构建。它本质上是一个 UIView
,它包裹了一个 UILabel
、一个 UITextField
和一个用来显示输入是否激活的 UIView
。
样式
以下是一些您可以访问并自定义样式的属性
input.label
公开UILabel
对象,您可以直接自定义甚至移除。input.inputColor
是文本框的背景颜色。input.backgroundColor
是整个元素的背景颜色。input.activeColor
是小竖直指示器的颜色,默认为蓝色。input.indicatorWidth
是活动指示器的宽度,默认为2.0。input.forcedLabelWidth
允许您强制使标签具有一定的宽度。这在您有多个输入框且希望所有文本框的左侧对齐时很有用。input.placeHolder
设置文本输入的占位符。input.font
设置文本输入的字体。input.textColor
设置文本输入的颜色。input.layer
如果需要执行更深入的定制,它为您提供了整个元素的设计层。input.inputLayer
为您提供了文本输入的设计层。input.keyboardType
允许您为文本输入设置键盘类型(注意这通常自动设置,但您可以手动设置以覆盖它)。input.layoutMargins
允许您自定义元素内的边距(从UIActiveInput
的边缘到标签和文本输入之间的空间)。
还有更多无法在此列出...
UIActiveInput
是UITextField
的一个子类,而UITextField
是UIView
的一个子类,因此在UIActiveInput
中也找到了您在这些类上期望找到的所有属性和方法。
文本输入代理
input.delegate
允许您为嵌入式的文本框指定代理。
触摸事件
默认情况下,如果在UIActiveInput
上的任何地方(包括标签)点击,文本框将变为响应者。您也可以通过调用input.becomeFirstResponder()
和input.resignFirstResponder()
来程序化地这样做。
无障碍访问
UIActiveInput
为您提供所有标准的访问性功能,并将所有这些调用传递给嵌入的文本输入(盲用户应“看到” UIActiveInput
就像常规的 UITextField
一样)。
一个简单的表单
CGSize 表单
以下是一个包含两个输入的非常简单的表单示例。此表单的目标是收集一个 CGSize
,一个用于宽度,一个用于高度。
class ViewController : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//...
let form = UIQuickFormView<CGSize>()
view.addSubview(form)
// Form styling and layout
form.backgroundColor = .green
form.layer.cornerRadius = 3.0
form.translatesAutoresizingMaskIntoConstraints = false
form.topAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor, constant: 10.0).isActive = true
form.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor, constant: 8.0).isActive = true
form.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor, constant: -8.0).isActive = true
let widthInput = UIActiveInput<CGFloat>(label: "WIDTH")
let heightInput = UIActiveInput<CGFloat>(label: "HEIGHT")
// Bind the inputs
let widthInputId = form.bind(input: widthInput) { (size: inout CGSize, input: UIActiveInput<CGFloat>) in
guard let width = input.output else {
// handle any errors here
return (false, nil)
}
size.width = width
return (true, nil)
}
let heightInputId = form.bind(input: heightInput) { (size: inout CGSize, input: UIActiveInput<CGFloat>) in
guard let height = input.output else {
// handle any errors here
return (false, nil)
}
size.height = height
return (true, nil)
}
// Lay out the inputs in the form (both inputs go on the same line here)
form.addRow([FormElement(widthInputId), FormElement(heightInputId)])
form.setRecommendedContentPriorities()
form.build() // sets up autolayout constraints for all the views within the form
// When you want to resolve the form to a CGSize:
var size = CGSize(width: 0, height: 0)
let success = form.resolve(model: &size) // returns a tuple (Bool, [Error])
}
}
处理错误
为处理糟糕的用户输入创建自己的错误类型。错误处理是在绑定处进行的。当表单解决时,您可以获取所有错误的数组。
enum PersonError : Error {
case tooOld, nilInput
}
...
let ageId = form.bind(input: ageInput) { (person, input) in
guard let age = input.output else {
return (false, PersonError.nilInput)
}
if age > 120 {
return (false, PersonError.tooOld)
}
person.age = age
return (true, nil)
}
...
let success = form.resolve(model: &person)
// success is a tuple (Bool, [Error])
if success.0 {
// everything looks good!
} else {
for error in success.1 {
...
}
}
将原生输入添加到表单中
您可以将任何类型的输入添加到 UIQuickFormView
中,包括原生和第三方。
let textInput = UITextField(frame: .zero)
let inputIndentifier = form.bind(input: textInput) { (..., input: UITextField) in
guard let text = input.text else {
...
}
...
}
form.addRow([..., FormElement(inputIndentifier), ...])
将视图添加到表单中
您可以将任何类型的视图添加到 UIQuickFormView
中。
let label = UILabel(frame: .zero)
let viewIndentifier = form.bind(view: label)
form.addRow([..., FormElement(viewIndentifier), ...])
自定义表单元素大小
您可以根据需要调整表单中每个元素在行中所占的水平空间。默认尺寸值为 1
。如果一行中的所有元素尺寸相同,则它们将平均分配水平空间。
form.addRow([FormElement(input, size: 2), FormElement(view, size: 1), ...])
占位符
UIQuickFormView
提供了创建占位符的便捷方式。
form.addRow([FormElement.makeSpacer(size: 3), FormElement(view, size: 1), ...])
即将推出
- 当前状态,无法在表单解决前知道是否有输入错误。一个当输入变化时通知调用代码是否出现错误的功能即将推出。表单将期望为每个输入绑定以及可选验证器。
- 自动绑定。
- 通用模态。
- 通用表视图和表视图控制器。