0

I'm looking for help inputting currency via a form. I would like to have:

  • Allow entry of a single decimal / comma currency separator
  • Limit entry to 2 digits past the decimal
  • Allow value editing
  • Limit maximum digits entered

There is a similar question asked 6 years ago at Limiting user input to a valid decimal number in Swift, but all the answers appear to be using viewDidLoad. I don't think that is feasible from within a form entry. If it is feasible, please show me how. Thanks

Form {
...

        // Enter entry amount
        Section {
            TextField("Enter Amount > " + curr, text: $moneyS)
                .keyboardType(.decimalPad)

        }
        
        // select entry save or cancel
        Section {
            Button(action: {
                self.showingAlert.toggle()
                
                ...

2 Answers2

1

Following the idea to achieve this, not covering all your points but I think is enough to procede by yourself:

class Test2Model: ObservableObject {
    @Published var currLimit: Double = 4.0
    @Published var digitLimit: Int = 2
    
    func getCurr(str: String) -> String{
        
        guard let currInserted = Double(str)
        else {
            return String(currLimit)
        }
        
        if currInserted <= currLimit {
            return String(currLimit)
        }
        return str
    }
}

struct Test2View: View {
    
    @ObservedObject private var test2Model = Test2Model()
    @State private var moneyS: String = ""
    
    var body: some View {
        
        Form {
        
            // Enter entry amount
            Section {
                TextField("Enter Amount > " + String(test2Model.currLimit), text: $moneyS)
                    .keyboardType(.decimalPad)
                    .onChange(of: moneyS, perform: { value in
                        guard let decimals = moneyS.components(separatedBy:".").last else {return}
                        if decimals.count > test2Model.digitLimit {
                            moneyS.removeLast()
                        }
                    })
            }
            
            // select entry save or cancel
            Section {
                Button(action: {
                    moneyS = test2Model.getCurr(str: moneyS)
                }, label: {
                    Text("Check")
                })
            }
        }
    }
}

struct Test2View_Previews: PreviewProvider {
    static var previews: some View {
        Test2View()
    }
}
Simone Pistecchia
  • 2,089
  • 2
  • 13
  • 21
0

you could try something like this:

struct ContentView: View {
let maxDigits = 6
let maxDecimals = 2
let allowedCharacters = CharacterSet.decimalDigits.union(CharacterSet(charactersIn: NumberFormatter().decimalSeparator))

@State var money: Double?
@State var moneyText = ""

var body: some View {
    VStack (spacing: 30) {
        Spacer()
        TextField("enter a number", text: $moneyText)
            .padding(.horizontal, 20)
            .keyboardType(.decimalPad)
            .onChange(of: moneyText) {
                // to prevent pasting non-valid text
                let txt = $0.filter { ".0123456789".contains($0) }
                if allowed(txt) {
                    money = Double(txt)
                    moneyText = txt
                } else {
                    moneyText = String(txt.dropLast())
                }
            }
        Text("Money value is: \(money ?? 0)")
        Spacer()
    }
}

func allowed(_ txt: String) -> Bool {
    let str = txt.trimmingCharacters(in: .whitespacesAndNewlines)
    switch str.components(separatedBy: ".").count - 1 {
    
    case 0:
        if str.count > maxDigits {
            return false
        }
        return allowedCharacters.isSuperset(of: CharacterSet(charactersIn: str))
        
    case 1:
        if str.starts(with: ".") && (str.count - 1) > maxDecimals {
            return false
        }
        let parts = str.split(separator: ".")
        if parts.count == 2 && (parts[1].count > maxDecimals) || (str.count - 1) > maxDigits {
            return false
        }
        return allowedCharacters.isSuperset(of: CharacterSet(charactersIn: str))
        
    default:
        return false
    }
}
}
workingdog
  • 3,125
  • 1
  • 5
  • 13