LateNate
LateNate

Reputation: 891

Percentage mask for textfield in Swift

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

Answers (1)

Paul
Paul

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

Related Questions