使用Binding<Int>与TextField SwiftUI

44

我目前正在构建一个页面,用于向本地数据库添加玩家信息。我有一组TextFields用于每个输入,这些输入与玩家结构中的元素相链接。

var body: some View {
        VStack {
            TextField("First Name", text: $player.FirstName)
                .textFieldStyle(RoundedBorderTextFieldStyle())
            TextField("Last Name", text: $player.LastName)
                .textFieldStyle(RoundedBorderTextFieldStyle())
            TextField("Email", text: $player.eMail)
                .textFieldStyle(RoundedBorderTextFieldStyle())
            TextField("Shirt Number", text: $player.ShirtNumber)
                .textFieldStyle(RoundedBorderTextFieldStyle())
            TextField("NickName", text: $player.NickName)
            .textFieldStyle(RoundedBorderTextFieldStyle())
            TextField("Height", text: $player.Height)
                .textFieldStyle(RoundedBorderTextFieldStyle())
            TextField("Weight", text: $player.Weight)
                .textFieldStyle(RoundedBorderTextFieldStyle())

            Button(action: {
                submitPlayer(player: self.player)T
            }) {
                Text("Submit")
            }

            Spacer()
        }
    }

我的播放器结构体是:

struct Player: Hashable, Codable, Identifiable {
    var id: Int
    var FirstName: String
    var LastName: String
    var NickName: String
    var eMail: String
    var ShirtNumber: Int
    var Height: Int
    var Weight: Int
}

问题在于ShirtNumber,Height和Weight都是Int值。当我将它们绑定到TextField时,会出现错误,提示“无法将类型为‘Binding’的值转换为预期参数类型‘Binding’”。我查阅了所有关于SwiftUI的资料,但似乎不可能有一个绑定整数值的TextField。
我的问题是,是否可以创建一个新类来扩展TextField,但只允许输入整数,并绑定一个整数变量,就像这样?
struct IntTextField: TextField {

    init(_ text: String, binding: Binding<Int>) {

    }
}

到目前为止,我只找到了一个部分回答我的问题(只接受整数输入),来自这个问题。我正在寻找一种将此与Binding<Int>结合使用的方法。

感谢您的帮助。

4个回答

81

实际上,你可以使用 TextField 绑定多种类型:

      @State var weight: Int = 0
      var body: some View {


       Group{
       Text("\(weight)")
       TextField("Weight", value: $weight, formatter: NumberFormatter())
    }}

8
这个解决方案的问题在于,TextField(_:value:formatter) 只有在按下回车键时才会反映到变量上。如果更改值并简单地移动到另一个字段,则不会反映在绑定中。 - SoliQuiD
5
请注意,此现已不推荐使用:https://developer.apple.com/documentation/swiftui/textfield/init(_:value:formatter:oneditingchanged:oncommit:)-6ab2i。 - Curiosity
2
这里使用的初始化程序 init(_:value:formatter:prompt:)IOS 16 中仍然有效。当输入时,更改的值也会适当地反映出来。 - burnsi

35
当然可以使用。
TextField("", value: $value, formatter: NumberFormatter())
//    .keyboardType(UIKeyboardType.decimalPad) // << uncomment for num pad
即使带有数字键盘,但这并不防止在此类文本字段中输入非数字字符,直到提交格式化程序以验证输入为止。也许苹果将来会给我们提供实时验证输入的可能性,但现在没有,...所以我更喜欢不同的方法。
这是我验证数字值(Int、Float、Double等)并限制特定类型的输入(例如,不允许输入超过Int最大允许值的值)的文本字段的方法。希望对其他人也有所帮助。(当然,可以根据使用需求配置字体、大小、颜色等设置)
struct NumberTextField<V>: UIViewRepresentable where V: Numeric & LosslessStringConvertible {
    @Binding var value: V
    
    typealias UIViewType = UITextField

    func makeUIView(context: UIViewRepresentableContext<NumberTextField>) -> UITextField {
        let editField = UITextField()
        editField.delegate = context.coordinator
        return editField
    }
    
    func updateUIView(_ editField: UITextField, context: UIViewRepresentableContext<NumberTextField>) {
        editField.text = String(value)
    }
    
    func makeCoordinator() -> NumberTextField.Coordinator {
        Coordinator(value: $value)
    }

    class Coordinator: NSObject, UITextFieldDelegate {
        var value: Binding<V>
        
        init(value: Binding<V>) {
            self.value = value
        }
        
        func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange,
                       replacementString string: String) -> Bool {
            
            let text = textField.text as NSString?
            let newValue = text?.replacingCharacters(in: range, with: string)
            
            if let number = V(newValue ?? "0") {
                self.value.wrappedValue = number
                return true
            } else {
                if nil == newValue || newValue!.isEmpty {
                    self.value.wrappedValue = 0
                }
                return false
            }
        }
        
        func textFieldDidEndEditing(_ textField: UITextField, reason: UITextField.DidEndEditingReason) {
            if reason == .committed {
                textField.resignFirstResponder()
            }
        }
    }
}

struct TestTextFieldWithNumbers: View {
    @State private var value = 0
    var body: some View {
        VStack {
            Text("Current value: \(value)")
            Divider()
            TextField("", value: $value, formatter: NumberFormatter())
//                .keyboardType(UIKeyboardType.decimalPad)
            Divider()
            NumberTextField(value: $value)
                .frame(height: 32)
        }
    }
}

struct TestTextFieldWithNumbers_Previews: PreviewProvider {
    static var previews: some View {
        TestTextFieldWithNumbers()
    }
}

哇塞!太完美了。只是缺少一行代码:editField.keyboardType = .numberPad 如果这是一个数字输入框,你应该启用数字键盘。谢谢。 - Duck
这似乎是更为健壮的解决方案,尽管"笨重"。 - Mike Williamson
这个已经不再起作用了:https://dev59.com/9r7pa4cB1Zd3GeqPzXmI - Cinn

8

只是为了更加明显。

将此更改为:

TextField("", text: $intvalue)

对于那个。
TextField("Value", value: $amount, formatter: NumberFormatter())

1

我试图在没有UIKit的情况下使其工作,因为我正在尝试构建一个小型macOS应用程序,所以UIKit不存在,因此找到了一种解决方案,它使用两个变量(不是最干净的但有效)

变量声明:

@State var minutes = 0
@State var minutesString = "0"

文本字段和步进器:

HStack {
    TextField("minutes", text: self.$minutesString)
        .onReceive(Just(self.minutesString)) { newValue in
            let filtered  = newValue.filter { $0.isNumber }
            if filtered != newValue {
                self.minutesString = filtered
                self.minutes = Int(filtered) ?? -1
            }                              
    }
                            
    Stepper("minutes", value: $minutes).onChange(of: self.hours) { newValue in
        self.minutesString = String(newValue)
    }
    .labelsHidden()
}

当textField或stepper被写入时,两个变量都会改变,并且不允许输入除数字以外的任何内容。

我对整个Swift的东西都有点新,所以非常感谢任何反馈。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接