UIKit 版本靠 shouldChangeCharactersIn 攔截每一次按鍵,SwiftUI 沒有這個 delegate,思路需要換一下:改成在 onChange 裡拿到變更後的完整字串,驗證、修正,再寫回去。
UIKit 版本:UITextField 限制輸入小數
實作
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
36
37
38
| struct DecimalTextField: View {
@Binding var text: String
var body: some View {
TextField("0.00", text: $text)
.keyboardType(.decimalPad)
.onChange(of: text) { _, newValue in
text = filtered(newValue)
}
}
}
private extension DecimalTextField {
func filtered(_ input: String) -> String {
// 只保留數字和小數點
let cleaned = input.filter { "0123456789.".contains($0) }
// 總長度限制
guard cleaned.count <= 10 else { return text }
// 只允許一個小數點
let parts = cleaned.components(separatedBy: ".")
guard parts.count <= 2 else { return text }
// 小數點後最多 2 位
if parts.count == 2, parts[1].count > 2 { return text }
// 首位是 "." 自動補成 "0."
if cleaned.hasPrefix(".") { return "0" + cleaned }
// 首位是 0 且下一位不是 ".",移除多餘的 0
if cleaned.count >= 2, cleaned.hasPrefix("0"), !cleaned.hasPrefix("0.") {
return String(cleaned.dropFirst())
}
return cleaned
}
}
|
與 UIKit 版的差異
| UIKit | SwiftUI |
|---|
| 攔截時機 | 按鍵當下(字元尚未寫入) | 變更後(拿到完整新字串) |
| 復原方式 | return false 阻止 | 把舊值寫回 text |
| 自動補值 | 直接改 textField.text | 把新值寫回 text |
| 程式碼位置 | delegate 方法 | onChange + private func |
SwiftUI 的 onChange 是在字串已經改變後才觸發,所以不能「阻止」輸入,只能「修正」回合法值。這是兩個版本最根本的差異。
使用方式
1
2
3
4
5
6
7
8
| struct ContentView: View {
@State private var amount = ""
var body: some View {
DecimalTextField(text: $amount)
.padding()
}
}
|
本文使用 Claude 共同完成