In my iPad app, I noticed different behavior between iOS 6 and iOS 7 with UITextFields.
I create the UITextField as follows:
UIButton *theButton = (U
Old question but all the above solutions seem overly complicated. Here is how I solved the issue:
I subscribed to two textfield events ->
On TextFieldEditingDidBegin, I simple set textField.textAlignment to UITextAlignmentLeft. On TextFieldEditingEnded, I set textField.textAlignment back to UITextAlignmentRight.
This worked flawlessly for me and I feel like its not a hack. Hope it helps!
I solved this issue in my app using an by using a left-aligned text field, and then used AutoLayout to align the entire text field to the right. This simulates a right-aligned text field and handles trailing spaces without messing around with space characters etc.
The main hurdle in this approach is that UITextField does not update it's intrinsic content size as the text changes. To get around this I subclassed UITextField to automatically calculate intrinsic content size as the text changes. Here's my subclass:
@implementation PLResizingTextField
- (instancetype)init {
self = [super init];
if(self) {
[self addTarget:self action:@selector(invalidateIntrinsicContentSize) forControlEvents:UIControlEventEditingChanged];
}
return self;
}
- (CGSize)intrinsicContentSize {
CGSize size = [super intrinsicContentSize];
NSString *text = self.text.length ? self.text : self.placeholder;
CGRect rect = [text boundingRectWithSize:CGSizeMake(CGFLOAT_MAX,CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:self.font}
context:nil];
size.width = CGRectGetWidth(rect);
return size;
}
@end
And here's a fragment of my auto layout code, using the PureLayout library:
[textField autoPinEdgeToSuperviewEdge:ALEdgeTrailing
withInset:10];
[textField autoPinEdge:ALEdgeLeading
toEdge:ALEdgeTrailing
ofView:cell.textLabel
withOffset:10
relation:NSLayoutRelationGreaterThanOrEqual];
[textField setContentHuggingPriority:UILayoutPriorityDefaultHigh
forAxis:UILayoutConstraintAxisHorizontal];
Important points to note here:
NSLayoutRelationGreaterThanOrEqual
relation between the left edge of text field and the view to the left of it (or superview's left edge).Fix right aligned text space removing by replacing space with non-breaking space
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (textField.textAlignment == NSTextAlignmentRight) {
NSString *text = [textField.text stringByReplacingCharactersInRange:range withString:string];
textField.text = [text stringByReplacingOccurrencesOfString:@" " withString:@"\u00a0"];
UITextPosition *startPos = [textField positionFromPosition:textField.beginningOfDocument offset:range.location + string.length];
UITextRange *textRange = [textField textRangeFromPosition:startPos toPosition:startPos];
textField.selectedTextRange = textRange;
return NO;
}
return YES;
}
And vice versa
- (void)textFieldDidEndEditing:(UITextField *)textField
{
// Replacing non-breaking spaces with spaces and remove obsolete data
NSString *textString = [[textField.text stringByReplacingOccurrencesOfString:@"\u00a0" withString:@" "] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
textField.text = textString;
}
My following solution also takes care of the problem with the cursor jumping to the end when typing a space in the middle or beginning of the string. Also pasting a string is now processed correctly too.
I also put in a check for email address fields and other checks, but the interesting part is the last part. It works perfectly for me, have yet to find a problem with it.
You can directly copy/paste this in your project. Don't forget to implement the didBeginEditing and didEndEditing to replace the spaces with non-breaking spaces and back!
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (textField.textAlignment != NSTextAlignmentRight) //the whole issue only applies to right aligned text
return YES;
if (!([string isEqualToString:@" "] || string.length > 1)) //string needs to be a space or paste action (>1) to get special treatment
return YES;
if (textField.keyboardType == UIKeyboardTypeEmailAddress) //keep out spaces from email address field
{
if (string.length == 1)
return NO;
//remove spaces and nonbreaking spaces from paste action in email field:
string = [string stringByReplacingOccurrencesOfString:@" " withString:@""];
string = [string stringByReplacingOccurrencesOfString:@"\u00a0" withString:@""];
}
//special treatment starts here
string = [string stringByReplacingOccurrencesOfString:@" " withString:@"\u00a0"];
UITextPosition *beginning = textField.beginningOfDocument;
textField.text = [textField.text stringByReplacingCharactersInRange:range withString:string];
UITextPosition *start = [textField positionFromPosition:beginning offset:range.location+string.length];
UITextPosition *end = [textField positionFromPosition:start offset:range.length];
UITextRange *textRange = [textField textRangeFromPosition:start toPosition:end];
[textField setSelectedTextRange:textRange];
return NO;
}
All the answers above are awesome and very indicative! Especially big thanks to meaning-matters's answer below. Here's a tested Swift 2.0 version. Remember to assign the delegate of the UITextField to your ViewController! Happy coding.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if (textField == self.desiredTextField) {
var oldString = textField.text!
let newRange = oldString.startIndex.advancedBy(range.location)..<oldString.startIndex.advancedBy(range.location + range.length)
let newString = oldString.stringByReplacingCharactersInRange(newRange, withString: string)
textField.text = newString.stringByReplacingOccurrencesOfString(" ", withString: "\u{00a0}");
return false;
} else {
return true;
}
}
--
And here is Swift 3!
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if (textField == self.textfield) {
let oldString = textField.text!
let newStart = oldString.index(oldString.startIndex, offsetBy: range.location)
let newEnd = oldString.index(oldString.startIndex, offsetBy: range.location + range.length)
let newString = oldString.replacingCharacters(in: newStart..<newEnd, with: string)
textField.text = newString.replacingOccurrences(of: " ", with: "\u{00a0}")
return false;
} else {
return true;
}
}
Swift 4 version:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool{
if var text = textField.text, range.location == text.count, string == " " {
let noBreakSpace: Character = "\u{00a0}"
text.append(noBreakSpace)
textField.text = text
return false
}
return true
}