Как ограничить ввод в текстовое поле, чтобы принимать только числа от 0 до 10 в SwiftUI?

#ios #swift #swiftui

#iOS #swift #swiftui

Вопрос:

Я рассмотрел разные ответы на этот вопрос, но я в замешательстве. Я относительно новичок в Swift. В настоящее время я работаю над своим выпускным проектом для школы. Хотелось бы, чтобы кто-нибудь ответил на вопрос, но также углубился в детали. Текущее поведение соответствует только 0-9. Я не могу заставить его выполнять число 10.

 struct ContentView: View {
    
    // Private vars for tracking
    @State private var hoursSlept = 0;
    
    // *** What happens if the user enters something other than an integer/double?
    @State private var depressedMood = ""
    @State private var elevatedMood = ""
    @State private var anxietyMood = ""
    @State private var irritabilityMood = ""
    
    @State private var maxLength = 1;
    
    
    // Var for button
    @State private var enterButtonPress = false
    
    var body: some View {
        VStack {
            NavigationView {
                Form {
                    
                    
                    
                    // Hours slept last night (0-24)
                    Stepper(value: $hoursSlept, in: 0...24, label: {
                        Text("Hours slept last night: (self.hoursSlept)")
                        
                    })
                    
                    TextField("Depression 0-10", text: $depressedMood)
                        .keyboardType(.numberPad)
                        .onReceive(Just(depressedMood)) {
                            guard !$0.isEmpty else {
                                self.depressedMood = ""
                                return
                            }
                            if let value = Int($0.filter(.isWholeNumber).prefix(maxLength)) {
                                self.depressedMood = String(value)
                            }
                            
                        }
                    
                    
                    
                    TextField("Elevation 0-10", text: $elevatedMood)
                        .keyboardType(.numberPad)
                        .onReceive(Just(elevatedMood)) {
                            guard !$0.isEmpty else {
                                self.elevatedMood = ""
                                return
                            }
                            
                            if let value = Int($0.filter(.isWholeNumber).prefix(maxLength)) {
                                self.elevatedMood = String(value)
                            }
                        }
                    
                    
                    
                    TextField("Anxiety 0-10", text: $anxietyMood)
                        .keyboardType(.numberPad)
                        .onReceive(Just(anxietyMood)) {
                            guard !$0.isEmpty else {
                                self.anxietyMood = ""
                                return
                            }
                            if let value = Int($0.filter(.isWholeNumber).prefix(maxLength)) {
                                self.anxietyMood = String(value)
                            }
                        }
                    
                    
                    TextField("Irritability 0-10", text: $irritabilityMood)
                        .keyboardType(.numberPad)
                        .onReceive(Just(irritabilityMood)) {
                            guard !$0.isEmpty else {
                                self.irritabilityMood = ""
                                return
                            }
                            if let value = Int($0.filter(.isWholeNumber).prefix(maxLength)) {
                                self.irritabilityMood = String(value)
                            }
                        }
  

Комментарии:

1. Вы пытались создать Formatter и передать его в текстовое поле?

2. maxLength установлено значение 1 . Чего бы вы ожидали?

3. Когда я устанавливаю maxLength = 2, это делает числа больше 11.

4. Я не знаю, как создать форматировщик. Просматриваю его сейчас. Какие-нибудь ресурсы, которые вы знаете наверху, для моей ситуации?

5. Что вам нужно, так это сохранить последнее значение и установить максимальное значение. Проверьте, превышает ли текущее значение максимально допустимое, если true, верните его к последнему значению, в противном случае оставьте как есть.

Ответ №1:

Вот возможное решение — используйте ~= и проверьте, находится ли значение в диапазоне:

 struct ContentView: View {
    @State private var depressedMood = ""

    var body: some View {
        TextField("Depression 0-10", text: $depressedMood)
            .keyboardType(.numberPad)
            .onReceive(Just(depressedMood)) {
                guard let value = Int($0), 0...10 ~= value else {
                    self.depressedMood = ""
                    return
                }
                self.depressedMood = String(value)
            }
    }
}
  

Комментарии:

1. Я бы сохранил последнее значение вместо очистки поля

2. Правда, я только что сохранил решение OP, которое использует self.depressedMood = "" .

3. Не имеет смысла изменять содержимое текстового поля, когда вводится что-то недопустимое

Ответ №2:

Вам следует попробовать пользовательское текстовое поле, чтобы вы могли управлять логикой. Затем вы можете привязать его к любому текстовому полю, которое вы хотите.

 class TextBindingManager: ObservableObject {
    @Published var text = "" {
        didSet {
            guard let newValue = Int(text) else {
                text = oldValue
                return
            }
            
            if( !(newValue >= range.lowerBound) || !(newValue <= range.upperBound)) {
                text = oldValue
            }
        }
    }
    
    let range: Range<Int>

    init(limit: Range<Int> = 0..<10){
        range = limit
    }
}
  

Затем вы можете использовать его так, как вам нравится, привязав его

 struct MyView: View {
    
    @ObservedObject var textBindingManager = TextBindingManager()
    
    var body: some View {
            
       TextField("", text: $textBindingManager.text, onEditingChanged: { _ in }, onCommit: {})
    }
}
  

Комментарии:

1. guard let newValue = Int(text), range ~= newValue else {

Ответ №3:

вы могли бы использовать что-то простое вроде этого:

 struct ContentView: View {
    @State var depressedMood = ""
    var body: some View {
        VStack {
            TextField("0-10", text: $depressedMood)
                .keyboardType(.numberPad)
                .onChange(of: depressedMood) {
                    let txt = $0.filter("0123456789".contains)
                    if let num = Int(txt), num <= 10, txt.count <= 2 {
                        depressedMood = txt
                    } else {
                        depressedMood = String(txt.dropLast())
                    }
                }
        }
    }
}
  

Комментарии:

1. $0.filter("0123456789".contains)

2. да, более лаконично, это тоже будет хорошо работать. Я обновлю свой ответ этим вместе с добавленным ограничением текста.