Обнаружение backspace в пустом UITextField

Есть ли способ обнаружить, когда клавиша Backspace/Delete нажата на клавиатуре iPhone на UITextField, которая пуста? Я хочу знать, когда Backspace нажата, только если UITextField пуст.


Основываясь на предложении @Alex Reynolds в комментарии, я добавил следующий код при создании своего текстового поля:

[[NSNotificationCenter defaultCenter] addObserver:self
          selector:@selector(handleTextFieldChanged:)
              name:UITextFieldTextDidChangeNotification
            object:searchTextField];

Получено это уведомление (вызывается функция handleTextFieldChanged), но при этом я не нажимаю клавишу Backspace в пустое поле. Любые идеи?


Кажется, что возникает некоторая путаница вокруг этого вопроса. Я хочу получать уведомление при нажатии клавиши Backspace. Это. Но решение должно также работать, когда UITextField уже пуст.

Ответ 1

Это может быть длинный выстрел, но он может работать. Попробуйте установить текст текстового поля на символ пробела нулевой ширины \u200B. Когда backspace нажата в текстовом поле, которое кажется пустым, оно фактически удалит ваше пространство. Затем вы можете просто вставить пробел.

Может не работать, если пользователю удастся перемещать каретку слева от пробела.

Ответ 2

Swift 4:


Подкласс UITextField:

// MyTextField.swift
import UIKit

protocol MyTextFieldDelegate {
    func textFieldDidDelete()
}

class MyTextField: UITextField {

    var myDelegate: MyTextFieldDelegate?

    override func deleteBackward() {
        super.deleteBackward()
        myDelegate?.textFieldDidDelete()
    }

}

Реализация:

// ViewController.swift

import UIKit

class ViewController: UIViewController, MyTextFieldDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        // initialize textField
        let input = MyTextField(frame: CGRect(x: 50, y: 50, width: 150, height: 40))

        // set viewController as "myDelegate"
        input.myDelegate = self

        // add textField to view
        view.addSubview(input)

        // focus the text field
        input.becomeFirstResponder()
    }

    func textFieldDidDelete() {
        print("delete")
    }

}

Objective-C:


Подкласс UITextField:

//Header
//MyTextField.h

//create delegate protocol
@protocol MyTextFieldDelegate <NSObject>
@optional
- (void)textFieldDidDelete;
@end

@interface MyTextField : UITextField<UIKeyInput>

//create "myDelegate"
@property (nonatomic, assign) id<MyTextFieldDelegate> myDelegate;
@end

//Implementation
#import "MyTextField.h"

@implementation MyTextField

- (void)deleteBackward {
    [super deleteBackward];

    if ([_myDelegate respondsToSelector:@selector(textFieldDidDelete)]){
        [_myDelegate textFieldDidDelete];
    }
}

@end

Теперь просто добавьте MyTextFieldDelegate к UIViewController и установите UITextFields myDelegate на self:

//View Controller Header
#import "MyTextField.h"

//add "MyTextFieldDelegate" to you view controller
@interface ViewController : UIViewController <MyTextFieldDelegate>
@end

//View Controller Implementation
- (void)viewDidLoad {
    //initialize your text field
    MyTextField *input = 
     [[MyTextField alloc] initWithFrame:CGRectMake(0, 0, 70, 30)];

    //set your view controller as "myDelegate"
    input.myDelegate = self;

    //add your text field to the view
    [self.view addSubview:input];
}

//MyTextField Delegate
- (void)textFieldDidDelete {
    NSLog(@"delete");
}

Ответ 3

Обновление: см. ответ JacobCaraballo для примера, который переопределяет -[UITextField deleteBackward].

Отъезд UITextInput, в частности UIKeyInput имеет deleteBackward метод делегирования, который всегда вызывается при нажатии клавиши удаления. Если вы делаете что-то простое, тогда вы можете рассмотреть просто подклассификацию UILabel и сделать ее совместимой с протоколом UIKeyInput, как это сделано SimpleTextInput и этот пример использования iPhone UIKeyInput. Примечание: UITextInput и его родственники (включая UIKeyInput) доступны только в iOS 3.2 и более поздних версиях.

Ответ 4

Код, как показано ниже:

@interface MyTextField : UITextField
@end

@implementation MyTextField

- (void)deleteBackward
{
    [super deleteBackward];

    //At here, you can handle backspace key pressed event even the text field is empty
}

@end

Наконец, не забудьте изменить свойство Custom Class текстового поля на "MyTextField"

Ответ 5

Я создал другой способ проще, чем subclass. Даже его немного странно, но он работает нормально.

- (BOOL)textView:(UITextView *)textView 
        shouldChangeTextInRange:(NSRange)range 
        replacementText:(NSString *)text
{
    const char * _char = [text cStringUsingEncoding:NSUTF8StringEncoding];
    int isBackSpace = strcmp(_char, "\b");

    if (isBackSpace == -8) {
       // is backspace
    }

    return YES;
}

Это немного странно для результата сравнения -8. Возможно, я ошибаюсь в какой-то момент C Programming. Но его правильная работа;)

Ответ 6

Быстрая реализация:

import UIKit

protocol PinTexFieldDelegate : UITextFieldDelegate {
    func didPressBackspace(textField : PinTextField)
}

class PinTextField: UITextField {

    override func deleteBackward() {
        super.deleteBackward()

        // If conforming to our extension protocol
        if let pinDelegate = self.delegate as? PinTexFieldDelegate {
            pinDelegate.didPressBackspace(self)
        }
    }
}

Ответ 7

Попробуйте delegate

- (BOOL)textField:(UITextField *)textField 
        shouldChangeCharactersInRange:(NSRange)range 
        replacementString:(NSString *)string {

Затем проверьте, есть ли range.length == 1, который, как представляется, при backspace.

Ответ 8

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

Цель C:

- (BOOL)keyboardInputShouldDelete:(UITextField *)textField { return YES; }

Swift:

func keyboardInputShouldDelete(_ textField: UITextField) -> Bool { return true }

Ответ 9

Ответ Никласа Альвея помог мне с аналогичной проблемой

Я ограничивал запись определенным набором символов, но игнорировал обратные пространства. Поэтому я проверил range.length == 1 перед обрезкой NSString. Если это правда, я просто возвращаю строку и не обрезаю ее. См. Ниже

 - (BOOL) textField:(UITextField *)textField 
          shouldChangeCharactersInRange:(NSRange)range 
          replacementString:(NSString *)string
 {
     NSCharacterSet *nonNumberSet = 
      [[NSCharacterSet characterSetWithCharactersInString:@"0123456789."] 
         invertedSet];

    if (range.length == 1) {
       return string;
    }
    else {
       return ([string stringByTrimmingCharactersInSet:nonNumberSet].length > 0);
    }   
 }

Ответ 10

Yup, используйте метод ниже, чтобы обнаружить backspace, когда textField пуст.

Необходимо добавить UITextFieldDelegate

yourTextField.delegate = self (ДОЛЖЕН ТРЕБУЕТСЯ)

Swift:

func keyboardInputShouldDelete(_ textField: UITextField) -> Bool { 
    return true
}

Цель C:

- (BOOL)keyboardInputShouldDelete:(UITextField *)textField { 
    return YES; 
}

Ответ 11

Для тех, у кого есть проблемы с ответом Jacob, я внедрил свой подкласс текстового поля следующим образом: он отлично работает!

#import <UIKit/UIKit.h>

@class HTTextField;

@protocol HTBackspaceDelegate <NSObject>

@optional
- (void)textFieldDidBackspace:(HTTextField*)textField;
@end

@interface HTTextField : UITextField<UIKeyInput>

@property (nonatomic, assign) id<HTBackspaceDelegate> backspaceDelegate;

@end


#import "HTTextField.h"

@implementation HTTextField

- (void)deleteBackward {
    [super deleteBackward];
    if ([self.backspaceDelegate respondsToSelector:@selector(textFieldDidBackspace:)]){
        [self.backspaceDelegate textFieldDidBackspace:self];
    }
}

- (BOOL)keyboardInputShouldDelete:(UITextField *)textField {
    BOOL shouldDelete = YES;

    if ([UITextField instancesRespondToSelector:_cmd]) {
        BOOL (*keyboardInputShouldDelete)(id, SEL, UITextField *) = (BOOL (*)(id, SEL, UITextField *))[UITextField instanceMethodForSelector:_cmd];

        if (keyboardInputShouldDelete) {
            shouldDelete = keyboardInputShouldDelete(self, _cmd, textField);
        }
    }

    if (![textField.text length] && [[[UIDevice currentDevice] systemVersion] intValue] >= 8) {
        [self deleteBackward];
    }

    return shouldDelete;
}

@end

Ответ 12

Лучшее использование, которое я нашел для обнаружения backspace, обнаруживает, когда пользователь нажал обратное пространство в пустом UITextField. Например, если у вас есть "пузырящиеся" получатели в почтовом приложении, когда вы нажимаете backspace в UITextField, он выбирает последнего "пузырящегося" получателя.

Это можно сделать так же, как ответ Джейкоба Карабалло. Но в ответ Джейкоба, если у UITextField остался один символ, когда вы нажмете backspace, к моменту получения сообщения делегата, UITextField уже будет пустым, так что вы эффективно обнаруживаете backspace в текстовом поле с не более чем одним символом.

Собственно, если вы хотите обнаружить backspace на UITextField с ровно нулевыми символами (пустым), вы должны отправить сообщение в delegate перед вызовом super deleteBackward. Например:

#import "MyTextField.h"

//Text field that detects when backspace is hit with empty text
@implementation MyTextField

#pragma mark - UIKeyInput protocol
-(void)deleteBackward
{
  BOOL isTextFieldEmpty = (self.text.length == 0);
  if (isTextFieldEmpty) {
    if ([self.delegate 
         respondsToSelector:@selector(textFieldDidHitBackspaceWithEmptyText:)]) {

        [self.delegate textFieldDidHitBackspaceWithEmptyText:self];
        }
    }
    [super deleteBackward];
}
@end

Интерфейс для такого текстового поля будет выглядеть примерно так:

@protocol MyTextFieldDelegate;

@interface MyTextField : UITextField
@property(nonatomic, weak) id<MyTextFieldDelegate> delegate;
@end

@protocol MyTextFieldDelegate <UITextFieldDelegate>
@optional
-(void)textFieldDidHitBackspaceWithEmptyText:(MyTextField *)textField;
@end

Ответ 13

В iOS 6 метод deleteBackward вызывается на UITextField, когда нажата кнопка backspace, в том числе, когда поле пусто. Таким образом, вы можете подклассифицировать UITextField и предоставить свою собственную реализацию deleteBackward (также вызывая супер).

Я все еще поддерживаю iOS 5, хотя мне понадобится комбинация ответа Andrew и этого.

Ответ 14

:) только для заголовка "Обнаружение backspace", где я использую UIKeyboardTypeNumberPad.

Я также встречаюсь с тем же вопросом сегодня, и вот мой код, чтобы узнать это:

- (BOOL)textField:(UITextField *)textField 
        shouldChangeCharactersInRange:(NSRange)range 
        replacementString:(NSString *)string
{
    NSLog([NSString stringWithFormat:@"%d", [string length]]);
}

Поскольку с UIKeyboardTypeNumberPad пользователь может вводить только номер или обратное пространство, поэтому, когда длина строки равна 0, она должна быть обратным.

Надеюсь, что вышеизложенное поможет.

Ответ 15

Вместо того, чтобы пытаться предконструировать то, что БЫЛО в текстовом поле, или выяснить, какой специальный символ был введен в методе shouldChangeCharactersInRange, я бы предложил сделать следующее:

[self performSelector:@selector(manageSearchResultsDisplay) 
           withObject:nil 
           afterDelay:0];

Это позволяет вам вызвать метод непосредственно после завершения текущей операции. Что это круто, так это то, что к моменту его завершения модифицированное значение уже будет в UITextField. В этот момент вы можете просто проверить его длину и/или подтвердить на основе того, что там.

Ответ 16

Подкласс UITextField не работал у меня на iOS 8.3, deleteBackward никогда не вызывался.

Вот решение, которое я использовал, работает на всех версиях iOS 8 и также должно работать с другими версиями iOS

for textField in textFields {
            textField.text = " "
}

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
        if string == "" && textField.text == " "   {
            // Do stuff here
            return false
        }
        return true
}

Ответ 17

В.h файле добавьте делегат UIKeyInput

- (BOOL)keyboardInputShouldDelete:(UITextField *)textField {

if ([textField isEqual:_txtFirstDigit]) {

}else if([textField isEqual:_txtSecondDigit]) {
    [_txtFirstDigit becomeFirstResponder];

}else if([textField isEqual:_txtThirdDigit]) {
    [_txtSecondDigit becomeFirstResponder];

}else if([textField isEqual:_txtFourthDigit]) {
    [_txtThirdDigit becomeFirstResponder];
}
return YES;
}   

улучшенное форматирование

Ответ 18

Я реализовал аналогичное решение с небольшими улучшениями, которые скажут мне, что если текстовое поле имеет какое-либо значение, а пользователь задействует обратное пространство. Это полезно для моего случая, когда мне нужно сосредоточиться только на другом текстовом поле, если текстовое поле пуст, когда нажата обратная сторона.

protocol MyTextFieldDelegate : UITextFieldDelegate {
    func textFieldDidDelete(textField: MyTextField, hasValue: Bool)
}

override func deleteBackward() {
    let currentText = self.text ?? ""
    super.deleteBackward()
    let hasValue = currentText.isEmpty ? false : true
    if let delegate = self.delegate as? MyTextFieldDelegate {
        delegate.textFieldDidDelete(textField: self, hasValue: hasValue)
    }
}

Ответ 19

В самом популярном ответе отсутствует одна вещь - возможность определить, было ли текстовое поле пустым или нет.

То есть, когда вы переопределяете метод deleteBackwards() подкласса TextField, вы все равно не знаете, было ли текстовое поле уже пустым. (Как до, так и после deleteBackwards() textField.text! Является пустой строкой: "")

Вот мое улучшение, с проверкой на пустоту перед удалением.

1. Создайте протокол делегата, который расширяет UITextFieldDelegate

protocol MyTextFieldDelegate: UITextFieldDelegate {
    func textField(_ textField: UITextField, didDeleteBackwardAnd wasEmpty: Bool)
}

2. Подкласс UITextField

class MyTextField: UITextField {
    override func deleteBackward() {
        // see if text was empty
        let wasEmpty = text == nil || text! == ""

        // then perform normal behavior
        super.deleteBackward()

        // now, notify delegate (if existent)
        (delegate as? MyTextFieldDelegate)?.textField(self, didDeleteBackwardAnd: wasEmpty)
    }
}

3. Внедрите свой новый протокол делегата

extension MyViewController: MyTextFieldDelegate {
    func textField(_ textField: UITextField, didDeleteBackwardAnd wasEmpty: Bool) {
        if wasEmpty {
            // do what you want here...
        }
    }
}

Ответ 20

Что-то вроде этого:

- (BOOL)textField:(UITextField *)textField 
        shouldChangeCharactersInRange:(NSRange)range 
        replacementString:(NSString *)string       
{  
    if (![text hash] && ![textField.text length])  
        [self backspaceInEmptyTextField];  
}

конечно, хэш для одной символьной строки.

Ответ 21

Чтобы сохранить это просто, это единственное условие, которое нужно проверить

     if (range.length==1)

Ответ 22

+ (BOOL)detectBackspaceOnly:(NSString *)string
{
    for(int i=0 ; i<string.length ; i++){
        unichar caract = [string characterAtIndex:i];
        if(caract != ' ' && caract != '\n')
            return NO;
    }

    return YES;
}

Ответ 23

Использование метода TextField Delegate:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string

Добавьте следующий код в описанный выше метод для обнаружения события удаления

if(textField == YourTextField)
{
    if ([string length] == 0 && range.length > 0)
    {
        // Your Code after deletion of character
    }
}

Ответ 24

В UITextViewDelegate:

- (BOOL)               textView:(UITextView *)textView 
        shouldChangeTextInRange:(NSRange)range 
                replacementText:(NSString *)text
{
    if(text isEqualToString:@"");
    {
        NSLog(@"press backspace.");
    }
}

он работает нормально для меня.

обновление для китайского упрощенного пиньинь и китайского ввода рукописного ввода:

- (BOOL)               textView:(UITextView *)textView 
        shouldChangeTextInRange:(NSRange)range 
                replacementText:(NSString *)text
{
    if (range.length > 0 && [text isEqualToString:@""]) {
        NSLog(@"press Backspace.");
    }
    return YES;
}

в документе говорится:

"Если пользователь нажимает кнопку deleteKey, длина диапазона равна 1, а пустой строковый объект заменяет этот единственный символ."