If I re-select a field (Decimal Row) that is blank AND a validation error of required has been triggered. The first digit I press gives me a decimal and a 0 after it. I press 1 but it gives me 1.0 AND when I keep typing it gives me 1.01 Very weird and confusing. I want normal editing. I don't want the "auto" decimal showing up. I just need to check for 2 decimal places after I click away from the field. What am I missing? Can I create a 'pattern' type validation like javascript for numbers? Maybe just using the textField row. Thanks!
I have narrowed down that it occurs only when a validation rule has been broken. When row is invalid.
Do you have useFormatterDuringInput
enabled? Try setting it to false?
@mats-claassen Thanks for the reply! I solved the (to me at least) unexpected behavior by .validateOnDemand and I run the row.validate() method on onCellHighlightedChange
but I don't have it run the first time I touch the cell.
.onCellHighlightedChanged({ (cell, row) in
if self.touchCount > 0 {
row.validate()
} else {
self.touchCount = 1
}
I still don't understand why... but with the above code I do not get the unexpected behavior. Thanks!
@mats-claassen How can I have a 'DecimalRow' but not allow it to format the number ever. No auto formatting. I can manually check to see if enough decimals have been placed using a Rule Closure and .significantFractionalDecimalDigits. Thanks!
You could try setting the row's formatter to nil but I am not sure that would work
I had this same issue and fixed it this way:
row.validationOptions = ValidationOptions.validatesOnDemand
row.useFormatterOnDidBeginEditing = false
row.useFormatterDuringInput = false
row.formatter = nil
Setting row.formatter = nil does not keep the row from adding a .0 to an interger. I put 87 but when I touch away it turns it into 87.0. I just want 87 I need my field to be required to be a number, with or without a decimal place. Thanks for your help. ZipCode row does not work because it allows text.
Still having this problem. @CutFlame's fix does not work for me, it's still difficult to re-edit a field because it keeps sticking in the period at a weird spot.
This input is for a (localized) money amount. Does anybody know how to setup a DecimalRow
as a working money input?
Have you tried using the CurrencyFormatter as in the examples?
Also if you want a row without decimal places you can use IntRow
with a Formatter for the money sign.
I tried the CurrencyFormatter from the examples, but it also was buggy with the period and doesn't allow formatting how I wanted.
In the end I got it working how I wanted using DecimalFormatter
with the following settings. RuleRequired()
seems to affect when the formatting runs. How it works now is you just enter numbers start with the cents, so typing 2845 is $28.45.
let formatter = DecimalFormatter()
formatter.locale = .current
formatter.numberStyle = .currency
formatter.currencySymbol = "$" // setting $ prevents including currency code ie. US, CAD
row.formatter = formatter
row.useFormatterDuringInput = true
row.useFormatterOnDidBeginEditing = true
// validation rules
row.add(rule:RuleRequired())
row.add(rule:RuleGreaterThan(min: 0.0))
row.validationOptions = .validatesOnChange
}.cellSetup { cell, _ in
cell.textField.keyboardType = .numberPad
}
Note I set to always use just $
no matter the locale, others probably wouldn't want this.
This was very frustrating for me too— I ran into the same exact issue building a decimal keypad for user bodyweight input.
Anyone ever fix this? I just posted a new issue going through showing the various ways the behavior is unexpected.
I have confirmed that the adding a .0 behavior happens only when validation is present. I will experiment if it only happens with some validation rules and not others.
This seems quite odd as it implies that validation is changing the value of the row in some circumstance. Which would cause the formatter to flip out on beginning the edit of the row?
The problem is RuleRequired. It seems that validation rules must match the type of value of the cell to function. Interesting restriction.
So, RuleGreaterOrEqualThan(min: 0.0)) and RuleGreaterThan(min: 0.0)) both solve the problem.
In this case RuleGreaterOrEqualThan validates for an empty field or a 0. RuleGreaterThan does not.
Interestingly, if you type a minus then a number, the appending of the .0 happens again. Meaning, whenever the field is viewed as invalid by any validation rule appending .0 happens. So if I set my minimum to anything above 1 (say 2) and I type 1, then the .0 is appended.
This is all happening with validation on change. If I do validationOnBlur, then if I enter the field when invalid and type something, .0 is appended. Crazy.
In my case RuleGreaterOrEqualThan(min: 0.0)
is sufficient. And if someone is typing something negative (which they can't do with the keypad provided) they are really using it wrong.
However, it doesn't seem like a Validation Rule should have any effect on content.
I opened #1819 showing many of the ways adding validation broke the DecimalRow. It seems like many of the formatter issues could be closed if validation didn't break the field.
If I were to guess, the problem is in that function. There are a couple of red flags to me in there that could be creating issues.
But, I think the biggest one is the way it defaults to converting a string to a Double. Whenever this is done a double always appends .0 to an integer. It's still not clear to me how that value is making it's way back to the textField, but it is. Basically, if you type any digit, it converts it to a float with .0 appended.
It's also not clear to me why it works correctly the first time, but never beyond that. I think this has something to do with the value setter. The only consistent workaround I can see is to make DecimalRow actually store things as strings which numberFormatters and other converters can convert to without appending the .0. But any double holding an int in swift shows with a .0 when logged.
Basically, internally convert string to numbers as necessary, but always store as strings in the value property. Ideally as the simplest unformatted string possible. No delimiters. It's the job of the formatter to convert that value to a friendly representation. (Be it monetary, decimal, scientific, whatever). I believe this would also simplify the field rows as most of them would simply be specifically configured formatters paired with a specific keyboard selection.
I may try a pull request for this depending on how hard of a job it is.
I figured out how this is happening. Validation triggers a cellUpdate to reconfigure the cell to display validation errors. This process takes the current row.value and sets it back into the UITextField.text variable.
When this happens it does not go through the UITextField did change process. Meaning the "raw" value is immediately displayed. When you type "1", it is stored as "1.0" in the float. Validation changes (in generating an error, or removing errors) trigger a cellUpdate (with validatesOnChange) and set that "1.0" to the textField.text property. Since editing is still in progress, the value isn't sanitized by any formatter. Technically, it doesn't have to be a validation change, anything that triggers a cellUpdate will cause the issue.
I think I found the fix. Going to submit a pull request. It is surprisingly simple.
Most helpful comment
I tried the CurrencyFormatter from the examples, but it also was buggy with the period and doesn't allow formatting how I wanted.
In the end I got it working how I wanted using
DecimalFormatter
with the following settings.RuleRequired()
seems to affect when the formatting runs. How it works now is you just enter numbers start with the cents, so typing 2845 is $28.45.Note I set to always use just
$
no matter the locale, others probably wouldn't want this.