Right aligned UITextField spacebar does not advance cursor in iOS 7

You'll have to replace the normal spaces with non-breaking spaces. It's best to trigger an action on a change event for this:

  1. Somewhere add an action for the UIControlEventEditingChanged event on your textfield:

    [myTextField addTarget:self action:@selector(replaceNormalSpacesWithNonBreakingSpaces)
                      forControlEvents:UIControlEventEditingChanged];
    
  2. Then implement the replaceNormalSpacesWithNonBreakingSpaces method:

    - (void)replaceNormalSpacesWithNonBreakingSpaces
    {
        self.text = [self.text stringByReplacingOccurrencesOfString:@" "
                                                         withString:@"\u00a0"];
    }
    

This is safer than using textField:shouldChangeCharactersInRange:replacementString:, because if you return NO from this method, you're actually saying that the specified text should not be changed. This will cause change events (like the IBActions textFieldEditingChanged: or the UITextField's UIControlEventEditingChanged event) to not be triggered.

Fix it everywhere:

If you want this fix for all your UITextFields you can create a category where you add these event actions when a UITextField is initiated. In the example below I also change the non-breaking spaces back to normal spaces when editing ended, so that possible problems with the non-breaking spaces won't occur when the data used somewhere else. Note that this example uses method swizzling so it might look a bit weird, but it's correct.

The header file:

//  UITextField+RightAlignedNoSpaceFix.h

#import <UIKit/UIKit.h>

@interface UITextField (RightAlignedNoSpaceFix)
@end

The implementation file:

//  UITextField+RightAlignedNoSpaceFix.m

#import "UITextField+RightAlignedNoSpaceFix.h"

@implementation UITextField (RightAlignedNoSpaceFix)

static NSString *normal_space_string = @" ";
static NSString *non_breaking_space_string = @"\u00a0";

+(void)load
{
    [self overrideSelector:@selector(initWithCoder:)
              withSelector:@selector(initWithCoder_override:)];

    [self overrideSelector:@selector(initWithFrame:)
              withSelector:@selector(initWithFrame_override:)];
}

/**
 * Method swizzles the initWithCoder method and adds the space fix
 * actions.
 */
-(instancetype)initWithCoder_override:(NSCoder*)decoder
{
    self = [self initWithCoder_override:decoder];
    [self addSpaceFixActions];
    return self;
}

/**
 * Method swizzles the initWithFrame method and adds the space fix
 * actions.
 */
-(instancetype)initWithFrame_override:(CGRect)frame
{
    self = [self initWithFrame_override:frame];
    [self addSpaceFixActions];
    return self;
}

/**
 * Will add actions on the text field that will replace normal 
 * spaces with non-breaking spaces, and replaces them back after
 * leaving the textfield.
 *
 * On iOS 7 spaces are not shown if they're not followed by another
 * character in a text field where the text is right aligned. When we
 * use non-breaking spaces this issue doesn't occur.
 *
 * While editing, the normal spaces will be replaced with non-breaking
 * spaces. When editing ends, the non-breaking spaces are replaced with
 * normal spaces again, so that possible problems with non-breaking
 * spaces won't occur when the data is used somewhere else.
 */
- (void)addSpaceFixActions
{

    [self addTarget:self action:@selector(replaceNormalSpacesWithNonBreakingSpaces)
               forControlEvents:UIControlEventEditingDidBegin];

    [self addTarget:self action:@selector(replaceNormalSpacesWithNonBreakingSpaces)
               forControlEvents:UIControlEventEditingChanged];

    [self addTarget:self action:@selector(replaceNonBreakingSpacesWithNormalSpaces)
               forControlEvents:UIControlEventEditingDidEnd];

}

/**
 * Will replace normal spaces with non-breaking spaces.
 */
- (void)replaceNormalSpacesWithNonBreakingSpaces
{
    self.text = [self.text stringByReplacingOccurrencesOfString:normal_space_string
                                                     withString:non_breaking_space_string];
}

/**
 * Will replace non-breaking spaces with normal spaces.
 */
- (void)replaceNonBreakingSpacesWithNormalSpaces
{
    self.text = [self.text stringByReplacingOccurrencesOfString:non_breaking_space_string
                                                     withString:normal_space_string];
}

@end

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;
    }
}

It would be a bit of a hack, but if you really need that to look the iOS6 way, you can replace space with non-breaking space as it's written. It's treated differently. Example code could look like this:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    // only when adding on the end of textfield && it's a space
    if (range.location == textField.text.length && [string isEqualToString:@" "]) {
        // ignore replacement string and add your own
        textField.text = [textField.text stringByAppendingString:@"\u00a0"];
        return NO;
    }
    // for all other cases, proceed with replacement
    return YES;
}

In case it's not clear, textField:shouldChangeCharactersInRange:replacementString: is a UITextFieldDelegate protocol method, so in your example, the above method would be in the viewcontroller designated by [textField setDelegate:self].

If you want your regular spaces back, you will obviously also need to remember to convert the text back by replacing occurrences of @"\u00a0" with @" " when getting the string out of the textfield.


Here's a solution that always works, also for pasting and editing (i.e. when you may add/delete texts with multiple spaces).

- (BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString*)string
{
    textField.text = [textField.text stringByReplacingCharactersInRange:range withString:string];
    textField.text = [textField.text stringByReplacingOccurrencesOfString:@" " withString:@"\u00a0"];

    return NO;
}

Don't worry about performance of doing stringByReplacingOccurrencesOfString every time; texts in UIs are very very short relative to CPU speed.

Then when you actually want to get the value from the text field:

NSString* text = [textField.text stringByReplacingOccurrencesOfString:@"\u00a0" withString:@" "];

So this is a nicely symmetrical.