iOS自动在文本字段中添加连字符

Posted

技术标签:

【中文标题】iOS自动在文本字段中添加连字符【英文标题】:iOS automatically add hyphen in text field 【发布时间】:2011-10-21 13:14:56 【问题描述】:

我正在学习 ios 开发,并且很难弄清楚控件的各种事件。对于测试,我有一个 UITextField,用户应该在其中输入以下格式的字符串:XXXX-XXXX-XXXX-XXXX

我希望能够检查字段中的文本在每个条目之后的长度,并查看它是否需要附加连字符。我已经为此设置了 IBAction 函数,但是当我将它分配给“Value Changed”事件时它什么也不做,当我在“Editing Did End”上设置它时它工作正常,但只会在用户退出时调用控制。

编辑:只是补充一下,“编辑更改”事件也会导致它崩溃。我假设这是堆栈溢出或文本设置再次调用事件处理程序的情况。

那么简而言之,有没有办法为用户每次在 UITextField 中输入字符时设置事件处理程序?

【问题讨论】:

你能提供一些你目前拥有的代码吗? 这几乎是 Formatting a UITextField for credit card input like (xxxx xxxx xxxx xxxx) 的重复,但并不完全是重复的,一些读者可能会发现我的回答很有用。 可以调整此答案以获得您所需要的。空格可以用连字符代替,其余部分将保持不变。 ***.com/questions/37190620/… 【参考方案1】:

请注意,前面的答案严重不足。天堂禁止您的用户输入错误的数字并敢于尝试删除它!公平地说,张贴者指出代码可能无法完美运行。但是,它甚至不会编译,所以买家要小心过滤器应该已经很高了。如果您修复编译错误并尝试代码,您会发现很容易得到与海报声明格式不匹配的输入。

这是我用来将文本字段限制为格式为 123-456-7890 的电话号码的解决方案。调整其他数字格式是微不足道的。注意传递的 NSRange 的使用。顺便说一句,即使使用数字虚拟键盘也需要拒绝非数字字符,因为用户仍然可以通过硬件键盘输入非数字。

另一个注释。我在第 4 位和第 7 位数字的输入之后添加连字符 ,以使删除数字更容易一些。如果在第 3 位和第 6 位之后添加,则必须处理删除悬空连字符的情况。下面的代码避免了这种用例。

// Restrict entry to format 123-456-7890
- (BOOL)                textField:(UITextField *)textField
    shouldChangeCharactersInRange:(NSRange)range
                replacementString:(NSString *)string 

  // All digits entered
  if (range.location == 12) 
    return NO;
  

  // Reject appending non-digit characters
  if (range.length == 0 &&
       ![[NSCharacterSet decimalDigitCharacterSet] characterIsMember:[string characterAtIndex:0]]) 
    return NO;
  

  // Auto-add hyphen before appending 4rd or 7th digit
  if (range.length == 0 &&
      (range.location == 3 || range.location == 7)) 
    textField.text = [NSString stringWithFormat:@"%@-%@", textField.text, string];
    return NO;
  

  // Delete hyphen when deleting its trailing digit 
  if (range.length == 1 &&
      (range.location == 4 || range.location == 8))  
    range.location--;
    range.length = 2;
    textField.text = [textField.text stringByReplacingCharactersInRange:range withString:@""];
    return NO;
  

  return YES;

【讨论】:

非常好的代码。在尝试实施一个小时后,我被难住了,然后我找到了你的解决方案。谢谢! 此代码中的一个小错误.. 如果用户输入 12 位数字,然后将光标移动到末尾以外的任何位置,那么他们可以输入超过字符限制 @liamnichols 对于这种情况可能会添加 else if (textField.text.length >= 12 && string.length != 0) return NO; The string.length != 0 必要的,因为它会否则一旦达到最大字符数就阻止删除 另一个小问题是,只要删除前面带有连字符的数字,光标就会重置到字符串的末尾 (当你移动光标时)当用户移动光标时,这个方法几乎会被抛弃【参考方案2】:

dingo sky 的回答很好,但为了帮助未来偶然发现此解决方案的人,存在一些问题。 Dingo 的解决方案允许您将长数字字符串粘贴到破坏委托的“规则”的字段中,因为它仅使用范围位置进行格式化和长度。 (您可以有超过 12 个字符并且没​​有连字符)。

简单的解决方案是计算结果字符串的长度,并每次重新格式化。

Dingo 答案的更新版本如下:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string 

    //calculate new length
     NSInteger moddedLength = textField.text.length-(range.length-string.length);

    // max size.
    if (moddedLength >= 13) 
        return NO;
    

    // Reject non-number characters
    if (range.length == 0 &&![[NSCharacterSet decimalDigitCharacterSet] characterIsMember:[string characterAtIndex:0]]) 
        return NO;
    

    // Auto-add hyphen before appending 4rd or 7th digit
    if ([self range:range ContainsLocation:3] || [self range:range ContainsLocation:7]) 
        textField.text = [self formatPhoneString:[textField.text stringByReplacingCharactersInRange:range withString:string]];
        return NO;
    

    return YES;


#pragma mark helpers

-(NSString*) formatPhoneString:(NSString*) preFormatted

    //delegate only allows numbers to be entered, so '-' is the only non-legal char.
    NSString* workingString = [preFormatted stringByReplacingOccurrencesOfString:@"-" withString:@""];

    //insert first '-'
    if(workingString.length > 3)
    
        workingString = [workingString stringByReplacingCharactersInRange:NSMakeRange(3, 0) withString:@"-"];
    

    //insert second '-'
    if(workingString.length > 7)
    
        workingString = [workingString stringByReplacingCharactersInRange:NSMakeRange(7, 0) withString:@"-"];
    

    return workingString;



-(bool) range:(NSRange) range ContainsLocation:(NSInteger) location

    if(range.location <= location && range.location+range.length >= location)
    
        return true;
    

    return false;

【讨论】:

如果用户移动光标,你仍然会出现奇怪的行为。例如,如果他们将它移到连字符之前,它不会被删除,并且光标会移到字符串的末尾。如果你搞砸了,你也可以连续使用 2 个连字符,等等。显然你不必担心这些边缘情况,但如果它会导致你的应用程序崩溃,那么考虑这一点很重要。即使粘贴是有效的,粘贴也根本不起作用。【参考方案3】:

对于这样的事情,我建议使用UITextFieldDelegate 来检测用户何时键入新字符。如下设置文本字段的委托:

[textField setDelegate:self];

然后,根据需要实现委托方法:

- (BOOL)textFieldShouldReturn:(UITextField *)textField 
    [textField resignFirstResponder]; // hide the keyboard
    return NO;

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string 
    // every time the length reaches four, it gets reset to 0 and a '-' is added.
    static int currentLength = 0;
    if ((currentLength += [string length]) == 4) 
        currentLength = 0;
        [textField setText:[NSString stringWithFormat:@"%@%@%c", [textField text], string, '-'];
        return NO;
    
    return YES;

这可能无法完美运行,但我希望它有所帮助!

【讨论】:

【参考方案4】:

这是我的方法,即使您移动光标和/或删除文本范围,甚至粘贴有效文本,我的方法也是有效的。基本上我的方法是每次重置文本并在适当的地方添加连字符。让它变得复杂的是,即使用户将光标移动到字符串的中间,它也会将光标的位置重置到正确的位置。不幸的是,有很多情况需要考虑。

我承认,对于这样一个简单的任务来说,它是非常复杂的(绝对可以使用重大清理)。也有点低效,但我们并没有在这里进行密集的计算。据我所知,这是这里最万无一失的解决方案;我欢迎任何人证明我错了。

-(BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string 
    if (range.location == 12 || (textField.text.length >= 12 && range.length == 0) || string.length + textField.text.length > 12 ) 
            return NO;
    

   // Reject appending non-digit characters
   if (range.length == 0 &&
       ![[NSCharacterSet decimalDigitCharacterSet] characterIsMember:[string characterAtIndex:0]]) 
       return NO;
   

    UITextRange* selRange = textField.selectedTextRange;
    UITextPosition *currentPosition = selRange.start;
    NSInteger pos = [textField offsetFromPosition:textField.beginningOfDocument toPosition:currentPosition];
    if (range.length != 0)  //deleting
        if (range.location == 3 || range.location == 7)  //deleting a dash
            if (range.length == 1) 
                range.location--;
                pos-=2;
            
            else 
                pos++;
            
        
        else 
            if (range.length > 1) 
                NSString* selectedRange = [textField.text substringWithRange:range];
                NSString* hyphenless = [selectedRange stringByReplacingOccurrencesOfString:@"-" withString:@""];
                NSInteger diff = selectedRange.length - hyphenless.length;
                pos += diff;
            
            pos --;
        
    

    NSMutableString* changedString = [NSMutableString stringWithString:[[textField.text stringByReplacingCharactersInRange:range withString:string] stringByReplacingOccurrencesOfString:@"-" withString:@""]];
    if (changedString.length > 3) 
        [changedString insertString:@"-" atIndex:3];
        if (pos == 3) 
            pos++;
        
    
    if (changedString.length > 7) 
        [changedString insertString:@"-" atIndex:7];
        if (pos == 7) 
            pos++;
        
    
    pos += string.length;

    textField.text = changedString;
    if (pos > changedString.length) 
        pos = changedString.length;
    
    currentPosition = [textField positionFromPosition:textField.beginningOfDocument offset:pos];

    [textField setSelectedTextRange:[textField textRangeFromPosition:currentPosition toPosition:currentPosition]];
    return NO;

或者:就用这个https://github.com/romaonthego/REFormattedNumberField

【讨论】:

【参考方案5】:

经过一番研究,我猜下面的解决方案可以自动以相等的间隔添加/删除一个新字符串。

解释: 1.插入一个新字符

    Text        :   XXXX-XXXX-
    Location    :   0123456789

    Objective   :   We've to insert new character's at locations 4,9,14,19,etc. Since equal spacing should be 4.

 Let's assume   y = The location where the new charcter should be inserted,
                z = Any positive value i.e.,[4 in our scenario] and 
                x = 1,2,3,...,n
 Then,
        =>  zx + x - 1 = y              e.g., [ 4 * 1 + (1-1) = 4 ; 4 * 2 + (2 - 1) = 9 ; etc. ]
        =>  x(z + 1) - 1 = y    
        =>  x(z + 1) = (1 + y)
        =>  ***x = (1 + y) % (z + 1)***         e.g., [ x = (1 + 4) % (4 + 1) => 0; x = (1 + 9) % (4 + 1) => 0 ]

 The reason behind finding 'x' leads to dynamic calculation, because we can find y, If we've 'z' but the ultimate objective is to find the sequence 'x'. Of course with this equation we may manipulate it in different ways to achieve many solutions but it is one of them.

 2. Removing two characters (-X) at single instance while 'delete' keystroke

    Text        :   XXXX-XXXX-
    Location    :   0123456789

    Objective   :   We've to remove double string when deleting keystroke pressed at location 5,10,15,etc. i.e., The character prefixed with customized space indicator

 Note: 'y' can't be zero


        =>  zx + x = y              e.g., [ 4 * 1 + 1 = 5 ; 4 * 2 + 2 = 10; 4 * 3 + 3 = 15; etc.]
        =>  x(z + 1) = y
        =>  ***x = y % (z + 1)***         e.g., [ x = (5 % (4 + 1)) = 0; x = (10 % (4 + 1)) = 0; etc. ]

Swift 中的解决方案:

let z = 4, intervalString = " "

func canInsert(atLocation y:Int) -> Bool  return ((1 + y)%(z + 1) == 0) ? true : false 

func canRemove(atLocation y:Int) -> Bool  return (y != 0) ? (y%(z + 1) == 0) : false 

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

        let nsText = textField.text! as NSString

        if range.length == 0 && canInsert(atLocation: range.location) 
            textField.text! = textField.text! + intervalString + string
            return false
        

        if range.length == 1 && canRemove(atLocation: range.location) 
            textField.text! = nsText.stringByReplacingCharactersInRange(NSMakeRange(range.location-1, 2), withString: "")
            return false
        

        return true
    

【讨论】:

首先,它过于复杂。其次,不考虑 shouldChangeCharactersInRange 可能会在字符串中间调用任意数量的字符。第三,从末尾一个一个删除字符时崩溃。【参考方案6】:

你可以试试这个:

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

它应该确实可以使用,您还应该发布一些代码。注册事件后,您应该检查字符串的长度并添加连字符。

【讨论】:

【参考方案7】:

当前接受的答案不考虑复制/粘贴到文本字段中

不使用代理的“shouldChangeCharactersInRange”,而是将文本字段中的 IBActionText Did Change 操作连接起来。然后添加以下代码:

- (IBAction)textFieldDidChange:(UITextField *)sender 
    if (sender.text.length > 0) 
        NSString *text = sender.text;
        text = [text stringByReplacingOccurrencesOfString:@"-" withString:@""];
        text = [text substringToIndex:MIN(20, text.length)];

        NSMutableArray *parts = [NSMutableArray array];
        int counter = 0;
        while (text.length > 0) 
            [parts addObject:[text substringToIndex:MIN(5, text.length)]];
            if (text.length > 5) 
                text = [text substringFromIndex:5];
             else 
                text = @"";
            
            counter ++;
        
        text = [parts objectAtIndex:0];
        [parts removeObjectAtIndex:0];
        for (NSString *part in parts) 
            text = [text stringByAppendingString:@"-"];
            text = [text stringByAppendingString:part];
        

        sender.text = text;
    

这是执行此操作的正确方法,因为如果用户将文本粘贴到文本字段中,您希望相应地格式化所有粘贴的文本(而不是一次只有一个字符)。

【讨论】:

以上是关于iOS自动在文本字段中添加连字符的主要内容,如果未能解决你的问题,请参考以下文章

iOS中文本字段和标签的自动布局

多年来在 IOS 中自动完成文本字段

如何在swift ios中自动将光标从一个文本字段移动到另一个文本字段?

目标 C 中 OTP 的自动移动键盘光标

如何在下拉列表中添加文本字段自动建议?

文本字段一个字符并自动更改为下一个文本字段?不起作用?