金融、電商類的 app 常常需要限制輸入金額,只允許輸入合法的小數——不能有多個小數點、小數點後只能兩位、開頭不能多餘的零。
UITextField 本身不提供這些限制,需要自己透過 delegate 的 shouldChangeCharactersIn 來攔截每一次輸入。
設定
1
2
| textField.keyboardType = .decimalPad
textField.delegate = self
|
decimalPad 讓鍵盤只顯示數字和小數點,但這只是 UI 層面的限制,使用者仍然可以透過貼上功能輸入任意字元,所以 delegate 的驗證不能省。
shouldChangeCharactersIn 實作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = textField.text else { return true }
// 1. 刪除事件直接放行
guard !string.isEmpty else { return true }
// 2. 只允許數字和小數點
guard "0123456789.".contains(string) else { return false }
// 3. 總長度限制(最多 10 位)
if text.count >= 10 { return false }
// 4. 小數點後最多 2 位
if let dotRange = text.range(of: ".") {
let dotIndex = NSRange(dotRange, in: text).location
if range.location - dotIndex > 2 { return false }
}
// 5. 首位是 0 時,下一個只能是小數點
if text == "0", string != "." {
textField.text = string
return false
}
// 6a. 首位直接輸入小數點,自動補成 "0."
if text.isEmpty, string == "." {
textField.text = "0."
return false
}
// 6b. 禁止重複輸入小數點
if text.contains("."), string == "." { return false }
return true
}
|
六種邊界情況
| 情況 | 處理方式 |
|---|
| 刪除(string 為空) | 直接放行 |
| 貼上非法字元 | 攔截,return false |
| 超過 10 位 | 攔截,return false |
| 小數點後超過 2 位 | 攔截,return false |
| 首位輸入 0 再輸入數字 | 替換成輸入的數字 |
| 首位輸入小數點 | 自動補成 0. |
| 重複輸入小數點 | 攔截,return false |
SwiftUI 版本另見:SwiftUI TextField 限制輸入小數
本文使用 Claude 共同完成