背景
在日常需求中,UITextField输入框需要做一些输入限制,防止用户乱输入,一般有两种方式,一种是在输入时输入非法字符无效,另一种是输入完成后,提示用户输入有误;这两种各有各的用处, 前一种是硬性规定那些不能输入,后一种则是对整个字符串进行规则(正则,数量等)校验。 下面介绍通过提取用户已完成的文本加上RxSwift监听机制完成输入限制的方法。
实现
// MARK: - 输入框扩展
extension UITextField {
/// 过滤预输入后的普通文本
var normalText: String? {
if let textRange = markedTextRange {
if textRange.isEmpty {
return text
} else {
if var text = text {
let location = self.offset(from: self.beginningOfDocument, to: textRange.start)
let length = self.offset(from: textRange.start, to: textRange.end)
let nsRange = NSRange(location: location, length: length)
if let range = Range.init(nsRange, in: text) {
text.replaceSubrange(range, with: "")
return text
}
}
return text
}
} else {
return text
}
}
/// 更新文本(不一样才更新)
func updateTextIfNeed(_ text: String?) {
if normalText != text {
self.text = text
}
}
/// 限制输入字数,0为不限制
func limit(_ count: Int) {
self.rx.controlEvent(.editingChanged).bind { [weak self] _ in
guard let self = self else { return }
if let normalText = self.normalText, normalText.count > count {
let index = normalText.index(normalText.startIndex, offsetBy: count)
let newText = String(normalText[..<index])
self.updateTextIfNeed(newText)
}
}.disposed(by: rx.disposeBag)
}
/// 限制中文输入
func limitChinese() {
// 名字中文输入限制
self.rx.text.bind { [weak self] _ in
guard let self = self else { return }
// 1. 获取到已完成的文本
if let text = self.normalText {
// 定义替换后的文本
var newText = ""
// 2. 过滤非中文
newText = text.filter({ c in
let isChinese = "\u{4e00}" <= c && c <= "\u{9fa5}"
return isChinese
})
// 3. 对比过滤后的文本是否不一致,不一致则更新
self.updateTextIfNeed(newText)
}
}.disposed(by: rx.disposeBag)
}
/// 限制输入字符
func limitCharaters(_ chars: Set<Character>) {
self.rx.text.bind { [weak self] _ in
guard let self = self else { return }
// 1. 获取到已完成的文本
if let text = self.normalText {
// 定义替换后的文本
var newText = ""
// 2. 过滤非中文
newText = text.filter({ c in
return chars.contains(c)
})
// 3. 对比过滤后的文本是否不一致,不一致则更新
self.updateTextIfNeed(newText)
}
}.disposed(by: rx.disposeBag)
}
/// 替换文本
func replace(orgin: String, new: String) {
self.rx.text.bind { [weak self] _ in
guard let self = self else { return }
if let text = self.normalText {
let newText = text.replacingOccurrences(of: orgin, with: new)
self.updateTextIfNeed(newText)
}
}.disposed(by: rx.disposeBag)
}
}