在开发过程中,我们经常会遇到在一个界面有多个UITextField,并且需要对其输入的文字有效性进行检查;为了更好的用户体验,要让用户可以在键盘上点击next跳到下一个UITextField,并且在最后一个UITextField时,可以点击return,例如下面的注册界面。基于这个需求,我为我们公司写了一个简单的库:HCTextField >>
注册界面.jpeg具体功能
-
文字有效性检查
- 是否为空
- 是否是邮件
- 文字的长度
-
用不同的边框的颜色表示不同状态:未选中时、选中时、有效性检查错误时
-
点击next调到下一个UITextField
具体功能实现
三种有效性检查
首先,我们要对文字进行三种有效性检查,我们使用OptionSet
来实现:
public struct HCTextFieldCheckType: OptionSet {
public let rawValue: Int
public init(rawValue: Int) {
self.rawValue = rawValue
}
/// don't perform any check
public static let none = HCTextFieldCheckType(rawValue: 1 << 0)
/// check if input is valid email
public static let email = HCTextFieldCheckType(rawValue: 1 << 1)
/// check content length
public static let length = HCTextFieldCheckType(rawValue: 1 << 2)
/// check if a put was missing
public static let notEmpty = HCTextFieldCheckType(rawValue: 1 << 3)
}
这里为什么不用enum
?因为我可能要对一个UITextField
进行多种检查,enum
只能表示其中一个可能,而OptionSet
可以包含多个可能,所以只能用OptionSet
。
重写resignFirstResponder
方法,在即将失去焦点的时候进行文有效性检查,同时更改边框的颜色:
open override func resignFirstResponder() -> Bool {
if checkType.contains(.email) && !isEmail(text) {
passedCheck = false
setBorder(for: .error)
textFieldDelegate?.textField(self, didCheckFor: .email, isSuccess: false, errorMessage: errorMessage)
} else if checkType.contains(.length) && !satisfiesLengthRequirement(text) {
passedCheck = false
setBorder(for: .error)
textFieldDelegate?.textField(self, didCheckFor: .length, isSuccess: false, errorMessage: errorMessage)
} else if checkType.contains(.notEmpty) && isEmpty(text) {
passedCheck = false
setBorder(for: .error)
textFieldDelegate?.textField(self, didCheckFor: .notEmpty, isSuccess: false, errorMessage: errorMessage)
} else {
passedCheck = true
setBorder(for: .normal)
textFieldDelegate?.textField(self, didCheckFor: .none, isSuccess: true, errorMessage: nil)
}
return super.resignFirstResponder()
}
点击next调到下一个UITextField
我们另外写了一个类HCTextFieldManager
来管理所有的UITextField
。在实例化HCTextFieldManager
时,我们先设置returnKeyType
的类型
/// Creates a HCTextFieldManager with several HCTextFields
///
/// - parameter textFields: the textFields you want to be managed, should be correctly ordered
public init(textFields: [HCTextField]) {
self.textFields = textFields
super.init()
if textFields.count == 1 {
textFields[0].delegate = self
} else if textFields.count > 1 {
for i in 0...(textFields.count - 1) {
textFields[i].delegate = self
// setup returnKeyType to ".next" except the last one (so user can customize it)
if i != (textFields.count - 1) {
textFields[i].returnKeyType = .next
}
}
}
}
实现UITextFieldDelegate
的这个方法:optional public func textFieldShouldReturn(_ textField: UITextField) -> Bool
,用户点击next的时候,让下一个textField获取焦点:
public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if textField.returnKeyType == .next,
let indexOfCurrentTextField = textFields.index(where: { return $0 == textField }),
indexOfCurrentTextField < textFields.count - 1 {
let nextTextField = textFields[indexOfCurrentTextField + 1]
_ = nextTextField.becomeFirstResponder()
} else if let lastTextField = textFields.last {
_ = lastTextField.resignFirstResponder()
delegate?.textFieldManager?(self, lastTextFieldDidPressReturnKey: lastTextField)
}
return true
}
这个库的实现的关键点主要是上面所讲到的内容,具体代码实现请看:HCTextField >>
完
有任何问题,欢迎大家留言!
欢迎加入我管理的Swift开发群:536353151
,本群只讨论Swift相关内容。
原创文章,转载请注明出处。谢谢!
网友评论