I am currently writing my first iOS application, Am I A Millionaire. One of the cool features I wanted to implement was to automatically format the currency that a user was entering, using the appropriate region formatting (e.g. commas to separate thousands in the US, and spaces if you were using South African Rand etc)
The code to format on the fly was pretty easy, but there was an issue of the cursor position being reset to the end of the string if you update a UITextField on the fly (e.g. field.text = @”some new value”). In order to get around this, I needed to find out where the cursor was before I changed the text, and then restore it afterwards.
As of iOS 5, this is a non-trivial process. At first it seemed simple – selectedTextRange returns a UITextRange object that has a start and end. Unfortunately, they are UITextPositions, not integers. This in a abstract class that you can’t get the members of to find out the index without violating Apple’s “don’t play with our privates” rule.
Fortunately, since UITextField conforms the UITextInput protocol. So, by comparing the current position to the end position, I was able to get the relative position of the cursor, and then restore it after setting the text. Sample code is below:
// Get the selected text range UITextRange *selectedRange = [self selectedTextRange]; // Calculate the existing position, relative to the end of the field (will be a - number) int pos = [self offsetFromPosition:self.endOfDocument toPosition:selectedRange.start]; // Change the text self.text = lastValidated; // Work out the position based by offsetting the end of the field to the same // offset we had before editing UITextPosition *newPos = [self positionFromPosition:self.endOfDocument offset:pos]; // Reselect the range, to move the cursor to that position self.selectedTextRange = [self textRangeFromPosition:newPos toPosition:newPos];
Hacky, but it works. This seems to be a common theme with iOS development. I wish Windows Phone 7 had the same audience, as .net is so much better to work with 🙂
NB: I used the offset from the end, as it was easier to deal with backspace. If the iPhone supported delete, then this code wouldn’t work. Also, it doesn’t work so well when the user selects an actual range and hits backspace.