概述
虽然 SwiftUI 本身提供了海量内置的原生视图供我们使用,但对于某些情况我们还需要根据实际需求“量体裁衣、专属定制”。
在日常的撸码场景中,我们有时需要限制文本框(TextField)中数字内容的输入,如何又简单又快速的实现它呢?
在本篇博文中,您将学到如下内容:
- 概述
- 1. 一个简单的“栗子”
- 2. 在 Binding 外部监听并限制
- 3. 拯救者:格式器(Formatter)
- 总结
闲言少叙,让我们马上开始行动吧!Let‘s go!!!😉
1. 一个简单的“栗子”
我们的实现非常简单:将 TextField 中输入数字的大小限制在 0-9999 之间。
先上代码:
struct ContentView: View {
@State var value = 0
var body: some View {
VStack {
Text("\(value)")
.font(.largeTitle.bold())
TextField("输入 0-9999 之间的数字", text: .init {
"\(value)"
} set: { newString in
guard let newValue = Int(newString) else { return }
value = min(9999, max(0, newValue))
})
.keyboardType(.decimalPad)
.textFieldStyle(.roundedBorder)
}
.padding()
}
}
在上面的代码中,我们利用一个自定义绑定(Binding)实现了目标数值的约束。可以看到,在绑定的 set 方法中我们将 value 限制在 0 - 9999 之间。
运行代码,看一下结果:
从结果可见:虽然最终输入值 value 被成功限制在了 0-9999 之间,但输入框(TextField)的输入内容并没有被限制住,这可不是我们想要的结果。看来在自定义绑定 set 方法中对 value 值的限定并没有让输入框本身的内容及时得到刷新。
这该如何是好呢?
2. 在 Binding 外部监听并限制
为了解决这一问题,我们需要在外部而不是绑定内部限制输入值。一种方法是利用 onChange() 修改器来监听并实施真正的约束:
struct ContentView: View {
@State var value = 0
var body: some View {
NavigationStack {
VStack {
Text("\(value)")
.font(.largeTitle.bold())
TextField("输入 0-9999 之间的数字", text: .init {
"\(value)"
} set: { newString in
guard let newValue = Int(newString) else { return }
value = newValue
})
.keyboardType(.decimalPad)
.textFieldStyle(.roundedBorder)
.onChange(of: value) {_, new in
value = min(9999, max(0, new))
}
}
.padding()
.navigationTitle("限制输入数值演示")
.toolbar {
Text("大熊猫侯佩 @ \(Text("CSDN").foregroundStyle(.red))")
.foregroundStyle(.gray)
.font(.headline.bold())
}
}
}
}
从上面代码可以看到,我们将原来 Binding 中对 value 值的限制逻辑“抽出来”放到了绑定外部的 onChange() 修改器中。这使得对 value 的更改会反过来引起输入框的重绘:
这样一来,我们就达到了限制 TextField 本身输入的效果,棒棒哒!💯
3. 拯救者:格式器(Formatter)
其实除了上面这种方法以外,我们还可以利用 TextField 自身提供的另一个构造器彻底“甩掉”自定义绑定来达到同样的效果:
不同于 TextField 默认的 构造器,这个构造器可以直接传入任意类型的绑定,只要它能被最后一个传入的格式器所转换(为 String)即可。
这意味着任何 Formatter 类的派生类都可以当做格式器传入进来。为什么能这样呢?这是因为 Formatter 类中有一个 string(for:) 方法可以将其它值转换为所需的字符串:
注意:如果 string(for:) 方法不能顺利完成转换(返回为nil),则 TextField 中的内容将保持不变。
于是乎,我们可以这样重构之前的代码了:
struct ContentView: View {
@State var value = 0
var body: some View {
NavigationStack {
VStack {
Text("\(value)")
.font(.largeTitle.bold())
TextField("输入 0-9999 之间的数字", value: $value, formatter: NumberFormatter())
.keyboardType(.decimalPad)
.textFieldStyle(.roundedBorder)
.onChange(of: value) {_, new in
value = min(9999, max(0, new))
}
}
.padding()
.navigationTitle("限制输入数值演示")
.toolbar {
Text("大熊猫侯佩 @ \(Text("CSDN").foregroundStyle(.red))")
.foregroundStyle(.gray)
.font(.headline.bold())
}
}
}
}
如您所见,在上面新的实现中我们使用系统内置的 NumberFormatter 格式化器完成了整型数值到字符串的转换:
编译并运行代码可以发现,结果和之前毫无二致,只不过逻辑变得更简单了:
现在,我们成功的对 TextField 的输入内容做了所需的限制,小伙伴们赶快给自己一个大大的赞吧!么么哒!
想要进一步系统地学习 Swift 开发的小伙伴们,可以来我的《Swift 语言开发精讲》专栏逛一逛哦:
- 《Swift 语言开发精讲》
总结
在本篇博文中,我们讨论了在 SwiftUI 中如何限制文本框(TextField)中数字内容的输入。我们稍后用两种方法解决了问题,任君选择。
感谢观赏,再会了!😎