我們知道 iOS 自帶的 UITextField
并沒有提供任何有關字數限制的屬性,如果想實現這個功能,最簡單的辦法大家有可能想到使用 delegate
實現 textField(_: , shouldChangeCharactersInRange _: , replacementString _:)
代理方法,在這個方法中我們只需得到最終替換完畢的結果即可對其進行字數或有效性的檢查,如果不符合要求就直接返回 false
。
這個代理方法將會在用戶修改輸入框內文字時觸發,傳入的 range
是用戶已選中的文本區間,string
則是用戶輸入的文字,而不是最終完整的文本。所以我們要先自己用新文本替換一下原來的文本。
代碼如下:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let replacedString = (textField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string)
if /* Validate replacedString */ {
return false
}
return true
}
注意這里有個小坑,就是如果你使用了 Emoji 表情或者其他西方文字,會出現運行時的異常,這是由于 Swift 和 Objective-C 對于字符串實現不同造成的,所以我并沒有使用 Swift 提供的
String
類型,而是將其強制轉換成NSString
,然后再進行字符串替換處理,以解決上述的問題。
但是問題并沒有解決,當我們使用中文鍵盤的時候,連續按兩下空格會輸入句號,而且上面的代理方法并不能攔截這個句號的輸入。所以我們只能曲線救國,由于 iOS 還提供了一個 Notification 叫做 UITextFieldTextDidChangeNotification
,監聽這個通知,然后在文本改變之后再進行一次檢驗。
我采用的方法是先修改上面的代理方法為:
func validate(string: String) -> Bool {
if string.isEmpty {
return true
}
if let regex = try? NSRegularExpression(pattern: "^[0-9]{0,4}.{0,1}[0-9]{0,2}$", options: .CaseInsensitive) {
return regex.matchesInString(string, options: .WithoutAnchoringBounds, range: NSMakeRange(0, string.characters.count)).count > 0
}
return false
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let replacedString = (textField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string)
if !self.validate(replacedString) {
return false
} else {
self.lastValidText = replacedString
}
return true
}
可以看到我使用了一個validate(_:)
函數對文本進行檢驗,這樣需求有變更時我們只需修改這個函數即可,本例子中我使用了一個正則表達式來檢驗用戶的輸入,當然實際使用時大家也可以去檢驗字數等。
然后我會保存每次合法的輸入,當輸入完畢時,下面方法將會調用:
func textFieldDidChangeText() {
if !self.validate(self.textField?.text ?? "") {
self.textField?.text = self.lastValidText
}
}
判斷文本修改完畢后的值是否合法,如果不合法就替換回上一次合法的輸入,這樣我們就完美的實現了字數、輸入限制了。
我們還可以對這些方法進行封裝:
然后使用時就可以這樣:
以上。