SwiftUI 入门趣谈:在文本框(TextField)内限制数字的输入

322 阅读4分钟

在这里插入图片描述

概述

虽然 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 的输入内容做了所需的限制,小伙伴们赶快给自己一个大大的赞吧!么么哒!

总结

在本篇博文中,我们讨论了在 SwiftUI 中如何限制文本框(TextField)中数字内容的输入。我们稍后用两种方法解决了问题,任君选择。

感谢观赏,再会了!8-)