是时候封装限制 UITextField、UITextView 的最大字符长度了
发布于: 2 小时前
在输入电话号码,设置昵称时,App 一般会设置一个允许输入的最大长度,来提示用户输入的内容过长了。
当用户输入的是纯英文字母、数字时,只需要在 UITextField、UITextView 的代理中判断用户输入的字符长度就可以了。但是在输入中文时,有一个从输入的拼音字符到联想到的汉字,再到确认汉字的过程。整个过程,UITextField 和 UITextView 都会通知代理用户输入的字符产生了变化。但是,在用户输入拼音字符时,也就是还没有确实汉字之前,就判断字符长度就会出现差错。
当用户输入中文时,如何知道 UITextField 或者 UITextView 处于输入了拼音,还处于汉字联想阶段呢?
可以通过输入框当前是否有高亮部分进行判断,如果有高亮部分,则意味着当前正在输入,这时就不进行字数统计;反之没有高亮部分时,就代表用户已经确认了一个汉字或者词语,这时候就可以做字符长度的判断了。
public protocol MaxLengthType: UITextInput {
/// 需要判断输入最大长度的字符串
///
/// UITextField 中 text 的定义为:
/// open var text: String? // default is nil
///
/// UITextView 中 text 的定义为:
/// open var text: String!
///
/// 所有通过 userInput 将两处不同的定义进行统一
var userInput: String? { get set }
}
extension UITextView: MaxLengthType {
public var userInput: String? {
get {
return self.text
}
set {
self.text = newValue
}
}
}
extension UITextField: MaxLengthType {
public var userInput: String? {
get {
return self.text
}
set {
self.text = newValue
}
}
}
private var maxLengthKey: Void?
private var observerKey: Void?
extension MaxLengthType {
/// 文本限制的最大长度
/// 为 nil 是表示不限制
public var maxLength: Int? {
get {
return getAssociatedObject(self, &maxLengthKey)
}
set {
setRetainedAssociatedObject(self, &maxLengthKey, newValue)
if newValue != nil {
if observer == nil {
observer = Observer(textInput: self,
textDidChange: { [weak self] (notification) in
self?.checkUserInputForMaxLength()
})
}
checkUserInputForMaxLength()
} else {
observer = nil
}
}
}
func checkUserInputForMaxLength() {
// 高亮部分为空时,并且 maxLength 存在时,再判断字符长度
guard self.markedTextRange == nil,
let maxLength = maxLength else {
return
}
guard let text = self.userInput,
text.count > maxLength else {
return
}
let endIndex = text.index(text.startIndex, offsetBy: maxLength)
userInput = String(text[text.startIndex..<endIndex])
}
var observer: Observer? {
get {
return getAssociatedObject(self, &observerKey)
}
set {
setRetainedAssociatedObject(self, &observerKey, newValue)
}
}
}
class Observer {
var textDidChange: (Notification) -> Void
init(textInput: UITextInput, textDidChange: @escaping (Notification) -> Void) {
self.textDidChange = textDidChange
if textInput is UITextField {
NotificationCenter.default
.addObserver(self, selector: #selector(textDidChange(notification:)),
name: .UITextFieldTextDidChange,
object: textInput)
} else if textInput is UITextView {
NotificationCenter.default
.addObserver(self,
selector: #selector(textDidChange(notification:)),
name: .UITextViewTextDidChange,
object: textInput)
}
}
deinit {
NotificationCenter.default.removeObserver(self)
}
@objc func textDidChange(notification: Notification) {
textDidChange(notification)
}
}
func getAssociatedObject<T>(_ object: Any, _ key: UnsafeRawPointer) -> T? {
return objc_getAssociatedObject(object, key) as? T
}
func setRetainedAssociatedObject<T>(_ object: Any, _ key: UnsafeRawPointer, _ value: T) {
objc_setAssociatedObject(object, key, value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
复制代码
完整的项目地址为MaxLength。
划线
评论
复制
发布于: 2 小时前阅读数: 3
芳
关注
还未添加个人签名 2017.10.17 加入
还未添加个人简介
评论