Reputation: 891
Ive been trying to achieve a percentage mask for a textfield that starts with the value 0.00%. If a user taps '1' it will become 0.01%. If they then tap 0 it becomes 0.10% and so on.
I've achieved this already with currency. And I've come very close using percentage but not managed to create exactly what I'm after.
So, field is originally set to 0.00%. I'd like to display the percentage symbol, I'd like the max number to be 100.00%, I'd like the max decimal places to be 2, and like the field to be updated when the user types and I'd like them to be able to press backspace to delete the last number entered. Oh and also when it reaches the maximum of 100.00, pressing another number does not reset the value, or do anything screwy.
Here is the code I've tried. I've also tried MANY variations:
func textFieldDidChanged(textField: UITextField) {
numberFormatter.numberStyle = .PercentStyle
numberFormatter.maximumFractionDigits = 2
numberFormatter.minimumFractionDigits = 2
numberFormatter.maximum = 100
let text = textField.text!.stringByReplacingOccurrencesOfString(numberFormatter.percentSymbol, withString: "").stringByReplacingOccurrencesOfString(numberFormatter.groupingSeparator, withString: "").stringByReplacingOccurrencesOfString(numberFormatter.decimalSeparator, withString: "")
textField.text = numberFormatter.stringFromNumber((text as NSString).doubleValue / 100.0)
}
I've looked all over, and spent way too long on this. Please help.
Upvotes: 5
Views: 2888
Reputation: 720
This is what I came up with (and for my application, its about as much effort I want to give). Two cases need to be handled: 1) inputting and 2) deleting. Handling the input was the easier portion (modified from a previous post). Handling the delete was trickier to realize. Basically when backspace is detected, move right one decimal place.
Handles input:
mInputRate.addTarget(self, action: #selector(rateTextFieldChanged), for: .editingChanged)
...
@objc func rateTextFieldChanged (_ textField : UITextField){
if let amountString = textField.text?.rateInputFormatting() {
textField.text = amountString
calculate()
}
}
and
extension String {
// formatting text for currency textField
func rateInputFormatting() -> String {
var number: NSNumber!
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 3
formatter.minimumFractionDigits = 3
formatter.maximum = 100
formatter.minimum = 0
var amountWithPrefix = self
// remove from String: "$", ".", ","
let regex = try! NSRegularExpression(pattern: "[^0-9]", options: .caseInsensitive)
amountWithPrefix = regex.stringByReplacingMatches(in: amountWithPrefix, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSMakeRange(0, self.characters.count), withTemplate: "")
let double = (amountWithPrefix as NSString).doubleValue
number = NSNumber(value: (double / 1000))
// if first number is 0 or all numbers were deleted
guard number != 0 as NSNumber else {
return ""
}
return "\(formatter.string(from: number)!)%"
}
}
This handles delete (backspace):
extension CalculatorVC : UITextFieldDelegate{
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let char = string.cString(using: String.Encoding.utf8)!
let isBackSpace = strcmp(char, "\\b")
if (isBackSpace == -92) {
//delete the second to last character
let text = textField.text!
let textDouble = text.replacingOccurrences(of: "%", with: "")
let doubleAdjusted = Double(textDouble)! / 10.0
//pad to three decimal places
let formatter = NumberFormatter()
formatter.maximumFractionDigits = 3
formatter.minimumFractionDigits = 3
formatter.roundingMode = .down
let num = NSNumber(value: doubleAdjusted)
let newText = formatter.string(from: num)!
textField.text = "\(newText)%"
}
return true
}
}
Upvotes: 1