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

iOS自动在文本字段中添加连字符,ios,objective-c,hyphen,Ios,Objective C,Hyphen,我正在学习iOS开发,很难弄清楚控件的各种事件。对于一个测试,我有一个UITextField,用户要在其中输入一个字符串,格式为:XXXX-XXXX-XXXX-XXXX 我希望能够检查字段中的文本在每个条目之后的长度,并查看是否需要在其后面附加连字符。我已经为此设置了IBAction函数,但当我将其分配给“值更改”事件时,它什么也不做,当我将其设置为“编辑结束”时,它可以正常工作,但仅当用户退出控件时才会调用 编辑:只需添加,“编辑已更改”事件也会导致它崩溃。我假设这是一个堆栈溢出,或者文本的设

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

我希望能够检查字段中的文本在每个条目之后的长度,并查看是否需要在其后面附加连字符。我已经为此设置了IBAction函数,但当我将其分配给“值更改”事件时,它什么也不做,当我将其设置为“编辑结束”时,它可以正常工作,但仅当用户退出控件时才会调用

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

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

您可以尝试以下方法:

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

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

对于类似的内容,我建议在用户键入新字符时使用来检测。按如下方式设置文本字段的委托:

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

这可能并不完美,但我希望它能有所帮助

请注意,前面的答案远远不够。上帝禁止你的用户输入错误的数字,并敢于尝试删除它!平心而论,该海报指出,该准则可能并不完美。但是,它甚至不会编译,所以过滤器应该已经很高了。如果您修复了编译错误并尝试了代码,您会发现您很容易得到与海报所述格式不匹配的输入

我使用了一个解决方案,将文本字段限制为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;
}

dingo sky的答案是好的,但是为了帮助那些在这个解决方案上遇到困难的未来人们,有几个问题。Dingo的解决方案允许您将长数值字符串粘贴到违反委托“规则”的字段中,因为它只使用范围位置来设置格式和长度。(可以有12个以上的字符,但不能有连字符)

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

野狗回答的更新版本如下:

- (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;
}
-(BOOL)textField:(UITextField*)textField应更改字符范围:(NSRange)范围替换字符串:(NSString*)字符串{
//计算新长度
NSInteger moddedLength=textField.text.length-(range.length string.length);
//最大尺寸。
如果(moddedLength>=13){
返回否;
}
//拒绝非数字字符
if(range.length==0&![[NSCharacterSet decimalDigitCharacterSet]charactermember:[string characterAtIndex:0]]{
返回否;
}
//在追加第4位或第7位之前自动添加连字符
if([自身范围:范围包含位置:3]| |[自身范围:范围包含位置:7]){
textField.text=[自格式化电话字符串:[textField.text stringByReplacingCharactersInRange:range with string:string]];
返回否;
}
返回YES;
}
#pragma标记帮助程序
-(NSString*)格式化PhoneString:(NSString*)预格式化
{
//委托只允许输入数字,因此“-”是唯一的非合法字符。
NSString*workingString=[preFormatted stringByReplacingOccurrencesOfString:@“-”with string:@”“];
//插入第一个“-”
如果(workingString.length>3)
{
workingString=[workingString StringByReplacingCharactersRange:NSMakeRange(3,0),带字符串:@“-”];
}
//插入第二个“-”
如果(workingString.length>7)
{
workingString=[workingString StringByReplacingCharactersRange:NSMakeRange(7,0),带字符串:@“-“];
}
返回工作字符串;
}
-(布尔)范围:(NSRange)范围包含位置:(NSInteger)位置
{
if(range.location=位置)
{
返回true;
}
返回false;
}

我的方法即使在移动光标和/或删除文本范围,甚至粘贴有效文本时仍然有效。基本上,我的方法是每次重置文本,并在适当的地方添加连字符。复杂的是,即使用户将光标移动到字符串的中间,它也会将光标的位置重置到正确的位置。不幸的是,有很多情况需要考虑。 我承认,对于这样一个简单的任务来说,它是非常复杂的(当然需要一次大的清理)。效率也有点低,但我们并没有在这里进行高强度的计算。据我所知,这是这里最简单的解决方案;我欢迎任何人证明我错了

-(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;
}
或者:仅使用此

当前接受的答案不考虑复制/粘贴到文本字段中 不要使用代表的“shouldChangeCharactersInRange”,而是将文本字段中的iAction文本确实发生了变化的操作连接起来。然后添加以下代码:

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

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

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

解释
    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. ]
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
    }