Reputation: 387
I'm looking for help inputting currency via a form. I would like to have:
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()
...
Upvotes: 0
Views: 174
Reputation: 36314
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
}
}
}
Upvotes: 0
Reputation: 2842
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()
}
}
Upvotes: 1