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

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

我创buildUITextField如下所示:

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中,当我input“hello world”时,当我点击“hello”后的空格键时,光标前进一个空格。

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

当iOS 7中的空格键被击中时,如何提前移动光标?

更新:

如果我将textField.textAlignment更改为UITextAlignmentLeft,则空间将出现在iOS 7中。如果可能,我想保持右alignment。

这将是一个黑客,但如果你真的需要这样看iOS6的方式,你可以用非破坏空间replace空间,因为它被写入。 它的处理方式不同。 示例代码可能如下所示:

 - (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中。

如果你想要回收常规空间,显然还需要记住在从文本字段中取出string时用@" "replace@"\u00a0"出现。

您将不得不用正常空间replace正常空间 。 最好在这个变化事件上触发一个动作:

  1. 某处为您的文本字段上的UIControlEventEditingChanged事件添加一个操作:

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

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

这比使用textField:shouldChangeCharactersInRange:replacementString:更安全textField:shouldChangeCharactersInRange:replacementString:因为如果你从这个方法返回NO ,你实际上是说不应该改变指定的文本。 这将导致更改事件(如IBActions textFieldEditingChanged:或UITextField的UIControlEventEditingChanged事件)不被触发。

无处不在:

如果您希望对所有UITextFields进行此修复,则可以在启动UITextField时创build一个添加这些事件操作的类别 。 在下面的例子中,当编辑结束时,我还将非中断空格更改回普通空格,以便在其他地方使用数据时不会出现非中断空格的问题。 请注意,这个例子使用方法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 

以上所有的答案是真棒,非常指示! 特别要感谢下面的 意义问题的答案 。 这是一个经过testing的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; } } 

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

 - (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:@" "]; 

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

我想出了一个UITextField类的子类并执行交换的解决scheme,无需在任何地方复制和粘贴代码。 这也避免了使用方法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:@" "]; } 

老问题,但所有上述解决scheme似乎过于复杂。 以下是我如何解决这个问题:

我订阅了两个文本字段事件 – >

  • TextFieldEditingDidBegin
  • TextFieldEditingEnded

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

这对我来说完美无瑕,我觉得它不是黑客。 希望能帮助到你!

三叉戟转化为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 } 

修复右alignment的文本空间删除通过replace空间与不间断的空间

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

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

在键入string中间或开始处的空格时,我的下面的解决scheme也会处理光标跳到最后的问题。 也粘贴一个string现在也正确处理。

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

你可以直接复制/粘贴到你的项目中。 不要忘记实施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; } 

我通过使用左alignment的文本字段在我的应用程序中解决了此问题,然后使用AutoLayout将整个文本字段向右alignment。 这模拟了一个右alignment的文本字段,并处理尾随空格,而不会与空间字符混淆。

这种方法的主要障碍是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. 在文本字段的左边缘和左边的视图(或者超级视图的左边缘)之间使用NSLayoutRelationGreaterThanOrEqual关系。

我已经使用了Jack Song对Swift 2 的回答了一段时间,直到我意识到非制动空间在其他地方以HTML呈现时会出现问题,并且在UITextView本身中出现换行问题。 所以,我已经改进了解决scheme,让非烙印字符马上清理。

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