7

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

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

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

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

4

7 回答 7

36

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

这是我用来将文本字段限制为格式为 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;
}
于 2012-07-10T16:53:23.823 回答
6

dingo sky's answer is good, but in the intrest of helping future people that stumble on this solution, there are a couple problems. Dingo's solution allows you to paste long numeric strings into the field that break the "rules" of the delegate, since it's only using the range location for formatting and length. (you can have more than 12 characters and not have the hyphens).

Simple solution is to calculate the length of the resulting string, and reformat it each time.

An updated version of Dingo's answer is below:

- (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;
}
于 2013-12-20T17:07:15.537 回答
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;
}

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

于 2011-08-06T18:11:49.680 回答
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

于 2014-05-10T23:52:09.363 回答
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
    }
于 2016-07-25T06:09:01.110 回答
1

你可以试试这个:

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

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

于 2011-08-06T17:32:32.533 回答
0

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

不要使用委托的“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;
    }
}

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

于 2014-07-18T00:31:05.730 回答