Cursor shifts to end on edit of formatted decimal textfield - Swift
Asked Answered
N

1

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!

Nsf answered 23/11, 2018 at 3:57 Comment(4)
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.Sumpter
@Sumpter Thanks for replying!Nsf
For a number like this “000012345” how should it look? “12,345” or “000,012,345”?Blisse
@Carpsen90 I have done regex testing before calling the above methods. So 00012345 is handled as 12,345 onlyNsf
P
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
    }
Procryptic answered 26/11, 2018 at 6:15 Comment(11)
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 endNsf
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 digitNsf
@Nsf please mail your project on my mail id. let me check what happen. because code work fine. Mail Id :- [email protected]Procryptic
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.Nsf
@Nsf please check my updated code. may it will be fix your issueProcryptic
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 }Nsf
@Nsf its my pleasure.Procryptic
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 issueNsf
@Nsf Try This one ------> else if string.count > 1 { currentPosition = currentPosition + formattedString.count }Procryptic
The cursor shifts to 2nd last position instead of last with thisNsf
The cut operation doesn't copy anything to the clipboard with this code. So unable to paste. Please modify the answer accordinglyNsf

© 2022 - 2024 — McMap. All rights reserved.