web-dev-qa-db-fra.com

Utiliser la liaison <Int> avec un TextField SwiftUI

Je suis en train de créer une page pour ajouter des informations sur les joueurs à une base de données locale. J'ai une collection de TextFields pour chaque entrée qui est liée à des éléments dans une structure de joueur.

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()
        }
    }

Ma structure de joueur est

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
}

Le problème est que ShirtNumber, Height et Weight sont tous des valeurs Int. Lorsque je les lie au TextField, j'obtiens une erreur disant Cannot convert value of type 'Binding<Int>' to expected argument type 'Binding<String>'. Tout ce que j'ai examiné à propos de SwiftUI indique qu'il est impossible d'avoir un TextField avec une valeur Int liée.

Ma question est, serait-il possible de créer une nouvelle classe qui étend TextField mais autorise uniquement les entrées Int et qui lie une variable Int, quelque chose comme ça?

struct IntTextField: TextField {

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

    }
}

Jusqu'à présent, tout ce que j'ai pu trouver est une réponse à une partie de ma question (n'acceptant que l'entrée Int) de this question. Je cherche un moyen de combiner cela avec le Binding<Int>.

Merci pour l'aide.

2
Bernard

Bien sûr, il est possible d'utiliser

TextField("", value: $value, formatter: NumberFormatter())
//    .keyboardType(UIKeyboardType.decimalPad) // << uncomment for num pad

et même avec le pavé numérique, mais cela n'empêche pas d'entrer des caractères non numériques dans un tel TextField, et jusqu'à ce que commit formatter ne soit pas appelé pour valider l'entrée. Peut-être Apple nous donnera la possibilité de valider la saisie à la volée à l'avenir, mais pas maintenant, ... donc je préfère une manière différente

Voici mon approche pour avoir un champ de texte pour les valeurs numériques (Int, Float, Double, etc.) qui valide la saisie et les limites de type spécifique (par exemple, ne permettez pas d'entrer des valeurs plus longtemps puis rentrez dans la valeur maximale autorisée Int). J'espère que ce serait utile pour quelqu'un aussi. (Bien sûr, des configurations telles que la police, la taille, les couleurs, etc. sont possibles en fonction des besoins d'utilisation)

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()
    }
}
1
Asperi