iOS автоматически добавляет дефис в текстовое поле - PullRequest
6 голосов
/ 06 августа 2011

Я изучаю разработку для iOS и с трудом разбираюсь в различных событиях для элементов управления. Для теста у меня есть UITextField, где пользователь должен ввести строку в формате: XXXX-XXXX-XXXX-XXXX

Я хочу иметь возможность проверять длину текста в поле после каждой записи и видеть, должен ли к нему добавляться дефис. Я настроил для этого свою функцию IBAction, но когда я назначаю ее событию «Значение изменено», она ничего не делает, она работает нормально, когда я устанавливаю ее на «Редактирование завершено», но она будет вызываться только при выходе пользователя управление.

Редактировать: просто добавьте, что событие "Редактирование изменено" также приводит к сбою. Я предполагаю, что это переполнение стека или что-то еще, когда настройка текста снова вызывает обработчик события.

Короче говоря, есть ли способ установить обработчик событий каждый раз, когда пользователь вводит символ в UITextField?

Ответы [ 7 ]

35 голосов
/ 10 июля 2012

Помните, что предыдущий ответ крайне неадекватен.Небеса запрещают вашему пользователю вводить неправильную цифру и осмеливаются пытаться удалить ее!Справедливости ради, автор отметил, что код может не работать идеально.Но тогда он даже не скомпилируется, поэтому фильтр покупателя должен быть уже высоким.Если вы исправите ошибку компиляции и попробуете код, то увидите, что вы легко можете получить ввод, который не соответствует заявленному формату автора.

Вот решение, которое я использовал для ограничения текстового поляномер телефона формата 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;
}
6 голосов
/ 20 декабря 2013

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;
}
5 голосов
/ 06 августа 2011

Для чего-то подобного я бы предложил использовать 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;
}

Это может не работать идеально, но я надеюсь, что это поможет!

2 голосов
/ 25 июля 2016

После небольшого исследования я думаю, что приведенное ниже решение может автоматически добавлять / удалять новую строку через равные интервалы.

Пояснение: 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
    }
2 голосов
/ 11 мая 2014

Вот мой подход, который работает, даже когда вы перемещаете курсор и / или удаляете диапазоны текста или даже вставляете допустимый текст в него. В основном мой подход заключается в том, чтобы каждый раз сбрасывать текст и добавлять дефисы в случае необходимости. Это усложняет то, что он также сбрасывает положение курсора в нужное место, даже если пользователь перемещает курсор в середину строки. К сожалению, есть много случаев, чтобы рассмотреть.

Признаюсь, это нелепо сложно для такой простой задачи (определенно можно было бы использовать основную очистку). Также немного неэффективно, но мы не делаем интенсивные вычисления здесь. Насколько я могу судить, это самое надежное решение здесь; Я приветствую любого, кто докажет, что я неправ.

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

1 голос
/ 06 августа 2011

Вы можете попробовать это:

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

Это должно действительно сработать, вы также должны опубликовать некоторый код.После регистрации на событие вы должны просто проверить длину строки и добавить дефис.

0 голосов
/ 18 июля 2014

Текущий принятый ответ не учитывает копирование / вставку в текстовое поле

Вместо использования делегата "shouldChangeCharactersInRange", подключите IBAction из текстового поля с действием Text 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;
    }
}

Это правильный способ сделать это, потому что если пользователь вставляет текст в текстовое поле, вы хотите соответствующим образом отформатировать весь вставленный текст (а не только один символ за раз).

...