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

时间:2011-08-06 17:06:51

标签: ios objective-c hyphen

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

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

编辑:只需添加,“编辑已更改”事件也会导致崩溃。我假设这是一个堆栈溢出或文本设置再次调用事件处理程序的东西。

简而言之,是否有任何方法可以在用户每次在UITextField中输入字符时设置事件处理程序?

7 个答案:

答案 0 :(得分:34)

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

这是我用于将文本字段限制为格式为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;
}

答案 1 :(得分:6)

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 :(得分:5)

对于类似这样的事情,我建议使用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;
}

这可能效果不好,但我希望它有所帮助!

答案 3 :(得分:2)

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

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

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

答案 4 :(得分:2)

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

<强>说明: 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
    }

答案 5 :(得分:1)

你可以试试这个:

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

它应该真的有用,你也应该发布一些代码。注册事件后,您应该检查字符串的长度并添加连字符。

答案 6 :(得分:0)

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

不使用委托的“shouldChangeCharactersInRange”,而是使用文本更改操作从文本字段连接 IBAction 。然后添加以下代码:

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

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