写点什么

是时候封装限制 UITextField、UITextView 的最大字符长度了

用户头像
关注
发布于: 2 小时前
是时候封装限制 UITextField、UITextView 的最大字符长度了

在输入电话号码,设置昵称时,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

用户头像

关注

还未添加个人签名 2017.10.17 加入

还未添加个人简介

评论

发布
暂无评论
是时候封装限制 UITextField、UITextView 的最大字符长度了