右对齐的UITextField空格键不会在iOS 7中前进光标

时间:2013-10-24 15:07:10

标签: ios objective-c ios7 uitextfield space

在我的iPad应用程序中,我注意到iOS 6和iOS 7与UITextFields之间存在不同的行为。

我按如下方式创建UITextField:

UIButton *theButton = (UIButton*)sender;
UITextField *textField = [[UITextField alloc] initWithFrame:[theButton frame]];

[textField setDelegate:self];
[textField setContentVerticalAlignment:UIControlContentVerticalAlignmentCenter];
[textField setContentHorizontalAlignment:UIControlContentHorizontalAlignmentRight];

textField.textAlignment = UITextAlignmentRight;
textField.keyboardType = UIKeyboardTypeDefault;

...

[textField becomeFirstResponder];

在iOS 6中,当我输入“hello world”时,当我在“你好”之后点击空格键时,光标会前进一个空格。

在iOS 7中,当我按空格键时光标不会前进。但是,当我在“世界”中键入“w”时,它会显示空格和w。

在iOS 7中按空格键时,如何使光标前进?

更新

如果我将textField.textAlignment更改为UITextAlignmentLeft,则该空间将显示在iOS 7中。如果可能,我希望将其保持正确对齐。

14 个答案:

答案 0 :(得分:43)

这将是一个黑客,但如果你真的需要这样看iOS6的方式,你可以用non-breaking space代替空格。它的处理方式不同。示例代码可能如下所示:

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

如果不清楚,textField:shouldChangeCharactersInRange:replacementString:UITextFieldDelegate协议方法,因此在您的示例中,上述方法将位于[textField setDelegate:self]指定的viewcontroller中。

如果您希望恢复常规空格,显然还需要记住在从文本字段中取出字符串时,将@"\u00a0"的出现位置替换为@" ",以便将文本转换回来。

答案 1 :(得分:13)

您必须使用non-breaking spaces替换普通空格。最好在此更改事件上触发操作:

  1. 在某处为文本字段中的UIControlEventEditingChanged事件添加操作:

    [myTextField addTarget:self action:@selector(replaceNormalSpacesWithNonBreakingSpaces)
                      forControlEvents:UIControlEventEditingChanged];
    
  2. 然后实施replaceNormalSpacesWithNonBreakingSpaces方法:

    - (void)replaceNormalSpacesWithNonBreakingSpaces
    {
        self.text = [self.text stringByReplacingOccurrencesOfString:@" "
                                                         withString:@"\u00a0"];
    }
    
  3. 这比使用textField:shouldChangeCharactersInRange:replacementString:更安全,因为如果从此方法返回NO,您实际上是在说不应更改指定的文本。这将导致更改事件(如IBActions textFieldEditingChanged:或UITextField的UIControlEventEditingChanged事件)不被触发。

    无处不在:

    如果您希望对所有UITextField进行此修复,则可以创建category,以便在启动UITextField时添加这些事件操作。在下面的示例中,我还在编辑结束时将非中断空格更改回正常空格,这样当数据在其他地方使用时,不会出现非中断空格的问题。请注意,此示例使用method swizzling,因此它可能看起来有点奇怪,但它是正确的。

    标题文件:

    //  UITextField+RightAlignedNoSpaceFix.h
    
    #import <UIKit/UIKit.h>
    
    @interface UITextField (RightAlignedNoSpaceFix)
    @end
    

    实施文件:

    //  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
    

答案 2 :(得分:11)

上面的所有答案都很棒,非常具有指示性!特别感谢meaning-mattersanswer below。这是经过测试的 Swift 2.0 版本。 请记住分配 UITextField的委托添加到您的ViewController中!快乐的编码。

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

}

-

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

答案 3 :(得分:5)

这是一个始终有效的解决方案,也适用于粘贴和编辑(即,您可以添加/删除具有多个空格的文本)。

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

不要担心每次执行stringByReplacingOccurrencesOfString的表现; UI中的文本相对于CPU速度非常短。

然后,当您确实想要从文本字段中获取值时:

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

所以这是一个很好的对称。

答案 4 :(得分:5)

我已经提出了一个解决方案,它可以对UITextField类进行子类化并执行交换,而无需在任何地方复制和粘贴代码。这也避免了使用方法sizzle来解决这个问题。

@implementation CustomTextField

-(id) initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];

    if( self ) {

        [self addSpaceFixActions];
    }

    return self;
}

- (void)addSpaceFixActions {
    [self addTarget:self action:@selector(replaceNormalSpaces) forControlEvents:UIControlEventEditingChanged];
    [self addTarget:self action:@selector(replaceBlankSpaces) forControlEvents:UIControlEventEditingDidEnd];
}


//replace normal spaces with non-breaking spaces.
- (void)replaceNormalSpaces {
    if (self.textAlignment == NSTextAlignmentRight) {
        UITextRange *textRange = self.selectedTextRange;
        self.text = [self.text stringByReplacingOccurrencesOfString:@" " withString:@"\u00a0"];
        [self setSelectedTextRange:textRange];
    }
}

//replace non-breaking spaces with normal spaces.
- (void)replaceBlankSpaces {
    self.text = [self.text stringByReplacingOccurrencesOfString:@"\u00a0" withString:@" "];
}

答案 5 :(得分:3)

triazotan的转换为Swift3。

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

    if (range.location == textField.text?.characters.count && string == " ") {
        let noBreakSpace: Character = "\u{00a0}"
        textField.text = textField.text?.append(noBreakSpace)
        return false
    }
    return true
}

答案 6 :(得分:2)

老问题,但上述所有解决方案似乎过于复杂。以下是我解决问题的方法:

我订阅了两个文本字段事件 - &gt;

  • TextFieldEditingDidBegin
  • TextFieldEditingEnded

在TextFieldEditingDidBegin上,我简单地将textField.textAlignment设置为UITextAlignmentLeft。 在TextFieldEditingEnded上,我将textField.textAlignment设置回UITextAlignmentRight。

这对我来说完美无缺,我觉得它不是一个黑客。希望它有所帮助!

答案 7 :(得分:2)

Swift 4版本:

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
}

答案 8 :(得分:1)

通过用不间断空格替换空格来修复右对齐文本空间

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

反之亦然

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

答案 9 :(得分:1)

这是来自@Jack Song的回答的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;
    }
}

答案 10 :(得分:0)

我的以下解决方案还解决了在字符串的中间或开头键入空格时光标跳到最后的问题。现在也正确处理字符串。

我还检查了电子邮件地址字段和其他检查,但有趣的部分是最后一部分。它对我来说很完美,还没有找到它的问题。

您可以直接在项目中复制/粘贴它。不要忘记实现didBeginEditing和didEndEditing来替换具有不间断空格的空格并返回!

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

答案 11 :(得分:0)

我使用左对齐文本字段在我的应用中解决了这个问题,然后使用AutoLayout将整个文本字段对齐到右边。这模拟了右对齐的文本字段并处理尾随空格而不会弄乱空格字符等。

此方法的主要障碍是UITextField在文本更改时不会更新其内在内容大小。为了解决这个问题,我将UITextField子类化为在文本更改时自动计算内在内容大小。这是我的子类:

@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

这是我的自动布局代码的一个片段,使用PureLayout库:

[textField autoPinEdgeToSuperviewEdge:ALEdgeTrailing
                            withInset:10];
[textField autoPinEdge:ALEdgeLeading
                toEdge:ALEdgeTrailing
                ofView:cell.textLabel
            withOffset:10
              relation:NSLayoutRelationGreaterThanOrEqual];
[textField setContentHuggingPriority:UILayoutPriorityDefaultHigh
                             forAxis:UILayoutConstraintAxisHorizontal];

此处需要注意的重点:

  1. 在文本字段中设置内容拥抱优先级
  2. 在文本字段的左边缘与其左侧的视图(或superview的左边缘)之间使用NSLayoutRelationGreaterThanOrEqual关系。

答案 12 :(得分:0)

我已经将{{3>}用于 Swift 2 一段时间,直到我意识到非制动空间在其他地方用HTML呈现时出现问题,以及换行变得混乱UITextView本身。所以,我已经改进了解决方案,立即清理非bracking字符。

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    if (textField == self.desiredTextField) {
       var oldString = textView.text!
       oldString = oldString.stringByReplacingOccurrencesOfString("\u{00a0}", withString: " ");
       let newRange = oldString.startIndex.advancedBy(range.location)..<oldString.startIndex.advancedBy(range.location + range.length)
       let alteredText = text.stringByReplacingOccurrencesOfString(" ", withString: "\u{00a0}")
       textView.text = oldString.stringByReplacingCharactersInRange(newRange, withString: alteredText)
       return false;
    } else {
       return true;
    }
}

答案 13 :(得分:0)

swift4

"abcdefghijklmnopABCDEFGHIJKLMNOP"