4

I am formatting a UITextField such that it becomes comma separated (Decimal style) while typing.

So 12345678 becomes 12,345,678

Now when I edit the UITextField, say I want to remove 5, at that time I tap after 5 and delete it but the cursor shifts to the end of the text immediately, that's after 8.

Here's my code which I have used to format the decimal while typing:

func checkTextField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    if ((string == "0" || string == "") && (textField.text! as NSString).range(of: ".").location < range.location) {
        return true
    }
    let allowedCharacterSet = NSCharacterSet(charactersIn: "0123456789.").inverted
    let filtered = string.components(separatedBy: allowedCharacterSet)
    let component = filtered.joined(separator: "")
    let isNumeric = string == component
    if isNumeric {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        let newString = (textField.text! as NSString).replacingCharacters(in: range, with: string)
        let numberWithOutCommas = newString.replacingOccurrences(of: ",", with: "")
        let number = formatter.number(from: numberWithOutCommas)
        if number != nil {
            var formattedString = formatter.string(from: number!)
            if string == "." && range.location == textField.text?.count {
                formattedString = formattedString?.appending(".")
            }
            textField.text = formattedString
        } else {
            textField.text = nil
        }
    }
    return false
}

It is called like this:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    let textFieldText = (textField.text! as NSString).replacingCharacters(in: range, with: string)
    let checkForCurrency = checkTextField(textField, shouldChangeCharactersIn: range, replacementString: string)
    return checkForCurrency
}

I have tried the following but in vain :

Solution 1

Solution 2

What could the reason for cursor shift be? It should be something like in this link Any help would be appreciated!

Mamta
  • 921
  • 1
  • 10
  • 28
  • 1
    Thats because you change text while editing and cursor position becomes invalid. You have to calculate a new cursor position and change it manually. Not an easy thing to do. Thats why its easier to leave number unformatted during editing and format only when users have finished editing. – Sulthan Nov 23 '18 at 07:56
  • @Sulthan Thanks for replying! – Mamta Nov 23 '18 at 08:45
  • For a number like this “000012345” how should it look? “12,345” or “000,012,345”? – ielyamani Nov 23 '18 at 09:22
  • 1
    @Carpsen90 I have done regex testing before calling the above methods. So 00012345 is handled as 12,345 only – Mamta Nov 23 '18 at 09:25

1 Answers1

3

update your custom function with :

func checkTextField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

        let textFieldText = (textField.text! as NSString).replacingCharacters(in: range, with: string)
        if ((string == "0" || string == "") && (textField.text! as NSString).range(of: ".").location < range.location) {
            return true
        }

        var currentPosition = 0
        if let selectedRange = textField.selectedTextRange {
            currentPosition = textField.offset(from: textField.beginningOfDocument, to: selectedRange.start)
        }

        let allowedCharacterSet = NSCharacterSet(charactersIn: "0123456789.").inverted
        let filtered = string.components(separatedBy: allowedCharacterSet)
        let component = filtered.joined(separator: "")
        let isNumeric = string == component
        if isNumeric {
            let formatter = NumberFormatter()
            formatter.numberStyle = .decimal
            let newString = (textField.text! as NSString).replacingCharacters(in: range, with: string)
            let numberWithOutCommas = newString.replacingOccurrences(of: ",", with: "")
            let number = formatter.number(from: numberWithOutCommas)

            if number != nil {
                var formattedString = formatter.string(from: number!)
                if string == "." && range.location == textField.text?.count {
                    formattedString = formattedString?.appending(".")
                }
                textField.text = formattedString

                if(textFieldText.count < formattedString?.count ?? 0){
                    currentPosition = currentPosition + 1
                }
            }else{
                textField.text = nil
            }
        }

        if(string == ""){
            currentPosition = currentPosition - 1
        }else{
           currentPosition = currentPosition + 1
        }

        if let newPosition = textField.position(from: textField.beginningOfDocument, offset: currentPosition) {
            textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
        }
        return false
    }
Tm Goyani
  • 406
  • 2
  • 9
  • Thanks for the reply. Still not fulfilling this criteria in my question : `Now when I edit the UITextField, say I want to remove 5, at that time I tap after 5 and delete it but the cursor shifts to the end of the text immediately, that's after 8.` Can you help me with this? My code is editing fine but only cursor gets shifted to the end – Mamta Nov 26 '18 at 07:38
  • The exact issue I am facing in your code is that once a comma is added to the text, the cursor is 1 position behind the last digit – Mamta Nov 26 '18 at 10:27
  • @Mamta please mail your project on my mail id. let me check what happen. because code work fine. Mail Id :- tmgoyani217@gmail.com – Tm Goyani Nov 28 '18 at 06:12
  • When you type `1234`, then comma gets added so it becomes `1,234` and cursor should be after `4` but it comes after `3` instead (due to comma). I think the else needs to be changed a bit to have a condition similar to this : `if newString.count(of: ",") < (textField.text?.count(of: ","))! { currentPosition = currentPosition + 1 } currentPosition = currentPosition + 1` I am still stuck on the same. – Mamta Nov 28 '18 at 12:28
  • @Mamta please check my updated code. may it will be fix your issue – Tm Goyani Nov 28 '18 at 12:53
  • Thanks for the answer. Quite close. I achieved it through : `if string == "" { currentPosition = currentPosition - 1 if textFieldString.count(of: ",") > (formattedString.count(of: ",")) { currentPosition = currentPosition - 1 } } else { if textFieldString.count(of: ",") < (formattedString.count(of: ",")) { currentPosition = currentPosition + 1 } currentPosition = currentPosition + 1 }` – Mamta Nov 29 '18 at 08:36
  • @Mamta its my pleasure. – Tm Goyani Nov 29 '18 at 08:57
  • Is there any way to allow paste of formatted string in the textfield. I am trying to do : `else if string.count > 1 { currentPosition = formattedString.count + 1 }` but i am facing cursor issue – Mamta Dec 24 '18 at 08:55
  • @Mamta Try This one ------> else if string.count > 1 { currentPosition = currentPosition + formattedString.count } – Tm Goyani Dec 24 '18 at 12:44
  • The cursor shifts to 2nd last position instead of last with this – Mamta Dec 24 '18 at 12:56
  • The cut operation doesn't copy anything to the clipboard with this code. So unable to paste. Please modify the answer accordingly – Mamta Jan 03 '19 at 09:24