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

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

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

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

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

【问题讨论】:

标签: ios objective-c hyphen


【解决方案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;
            }
        }
        

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

        【讨论】:

          猜你喜欢
          • 2015-09-10
          • 2016-10-06
          • 1970-01-01
          • 2015-12-21
          • 1970-01-01
          • 2011-02-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多