Để buộc UITextField chỉ chấp nhận nhập số (digits), có 2 cách chính: cấu hình Keyboard Type trên Storyboard, hoặc lọc ký tự trong Delegate.
Cách 1: Storyboard - Keyboard Type (Đơn giản, UX tốt)
- Chọn Text Field trong Storyboard.
- Attribute Inspector > Keyboard Type > chọn Number Pad (hoặc Decimal Pad cho số thập phân).
- User chỉ thấy bàn phím số – không thể nhập chữ.
Lưu ý: Number Pad không có nút "Return/Done" – cần thêm inputAccessoryView (toolbar) với nút Done để dismiss keyboard.
Cách 2: Delegate - Lọc ký tự (Programmatic validation)
class ViewController: UIViewController, UITextFieldDelegate {
@IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self
// Tùy chọn: keyboard type vẫn là default hoặc number pad
textField.keyboardType = .numberPad
}
func textField(_ textField: UITextField,
shouldChangeCharactersIn range: NSRange,
replacementString string: String) -> Bool {
// Cho phép xóa (backspace = string rỗng)
if string.isEmpty { return true }
// Chỉ cho phép chữ số 0-9
let allowedCharacters = CharacterSet.decimalDigits
let characterSet = CharacterSet(charactersIn: string)
return allowedCharacters.isSuperset(of: characterSet)
}
}
Cách 3: Formatter (Cho số có format: currency, decimal...)
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 2
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let currentText = textField.text ?? ""
guard let stringRange = Range(range, in: currentText) else { return false }
let updatedText = currentText.replacingCharacters(in: stringRange, with: string)
// Chỉ cho phép nếu parse được thành Number
return formatter.number(from: updatedText) != nil || updatedText.isEmpty
}
Complete Example (Cách 2 + Toolbar Done)
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
@IBOutlet weak var tf: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
tf.delegate = self
tf.keyboardType = .numberPad
addDoneToolbar()
}
func addDoneToolbar() {
let toolbar = UIToolbar()
toolbar.sizeToFit()
let doneBtn = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(dismissKeyboard))
let flexible = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
toolbar.items = [flexible, doneBtn]
tf.inputAccessoryView = toolbar
}
@objc func dismissKeyboard() { view.endEditing(true) }
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string.isEmpty { return true }
return CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: string))
}
}
So sánh
| Cách | Ưu điểm | Nhược điểm |
|---|---|---|
| Keyboard Type | UX tốt, không cần code, hệ thống handle | Không có nút Done (cần toolbar), không validate paste |
| Delegate filter | Kiểm soát hoàn toàn, validate paste, custom rule | Cần code, keyboard vẫn có thể hiện chữ nếu không set type |
Best Practice: Kết hợp cả 2: keyboardType = .numberPad + Delegate filter + inputAccessoryView Done toolbar.