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

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


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

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

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


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


person marcc    schedule 30.12.2009    source источник
comment
Я думаю, вы могли бы иметь в виду, только если UITextField пуст, а не только если клавиатура пуста ...?   -  person Steve Harrison    schedule 30.12.2009
comment
@ Стив Харрисон: спасибо. Обновил это.   -  person marcc    schedule 30.12.2009
comment
Я пытаюсь сделать что-то подобное, какое решение у вас тогда получилось? То, что я делаю, - это текстовое поле в режиме прокрутки, когда я набираю текст, отображаются предложения, а когда я щелкаю по нему, объект метки помещается слева от текстового поля. Заранее спасибо   -  person Dough    schedule 16.06.2010
comment
Решение 2011/11 для пустого поля UITextField с использованием обмана времени выполнения: bjhomer.blogspot.com/2011/11/   -  person Jano    schedule 17.11.2011
comment
До смешного хочется чего-то столь тривиального. Это не должно быть трудным.   -  person Adam Waite    schedule 21.01.2013
comment
См. Комментарий @MattDiPasquale ниже для лучшего решения.   -  person RocketMan    schedule 24.04.2014
comment
Решение JacobCaraballo - лучшее.   -  person ma11hew28    schedule 17.07.2014


Ответы (28)


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

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

person Andrew    schedule 30.12.2009
comment
Отлично. Я согласен с тем, что это довольно взломано, но я рассматривал что-то подобное как последнее средство. Спасибо за предложение. - person marcc; 31.12.2009
comment
@ Андрей, я решил применить именно такой подход. Потребовалось немного кода, но он, безусловно, эффективен. Спасибо за помощь, а не пытайтесь сказать мне, что я что-то делаю не так. - person marcc; 02.01.2010
comment
Этот метод может работать на iPhone ›3.1.3, но он хакерский и может сломаться в будущих версиях и т. Д. Я думаю, что нашел более чистое и стабильное решение для как определить нажатие клавиши удаления на клавиатуре iPhone / iOS. - person ma11hew28; 10.07.2011
comment
Обратите внимание, что вам также следует удалить пробел, когда текстовое поле не редактируется. В противном случае ваш текст-заполнитель не будет виден. - person Sulthan; 24.09.2011
comment
Я могу подтвердить, что удаление не обнаруживается, если пользователю удается переместить курсор влево от места. Если вы можете понять, как это исправить, вам также следует создать подкласс UITextField и реализовать от canPerformAction:withSender: до return NO для действий select: и selectAll:, когда текст равен строке @"\u200B". - person ma11hew28; 20.10.2011
comment
^ Я вспоминаю код, позволяющий определять местоположение курсора пользователя ... вы можете найти это, а затем вызвать метод becomeFirstResponder, если они перемещают курсор (чтобы он перемещался назад) - person Albert Renshaw; 15.01.2013
comment
Проблема в том, что это не работает для защищенных тестовых полей. Есть идеи, как с этим справиться? - person Kyle Clegg; 31.07.2013
comment
Извините, но это плохая идея. Это невероятно хакерский ответ, и он не должен быть принятым ответом с 30 или около того голосами. Вместо этого я бы создал подкласс UITextField, как упоминали некоторые другие комментаторы. - person Brian Sachetta; 26.06.2015

Swift 4:


Подкласс UITextField:

// MyTextField.swift
import UIKit

protocol MyTextFieldDelegate: AnyObject {
    func textFieldDidDelete()
}

class MyTextField: UITextField {

    weak 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")
    }

}

Цель-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");
}
person jacob    schedule 04.04.2013
comment
Это лучшее решение. Принятый ответ - это взлом. Цель C основана на подклассах, и это решение использует ее должным образом для решения проблемы. - person T.J.; 10.05.2013
comment
Что произойдет с моими существующими методами делегата в ViewController классе, если я установлю делегата своего текстового поля на подкласс UITextField вместо UIViewController - person rohan-patel; 15.08.2014
comment
Это явно не работает в ios8 прямо сейчас из-за того, что похоже на ошибку Apple: devforums. apple.com/message/1045312#1045312 - person chug2k; 20.09.2014
comment
Я использовал обходное решение для ошибки ios8, как в моем ответе, и оно сработало. Это может быть полезно тем, кто ищет решение. - person furkan3ayraktar; 21.10.2014
comment
Спасибо за отличный ответ! - person OxenBoxen; 28.09.2015
comment
Этот ответ также предоставляет простой и не требующий взлома способ работы с клавишей Backspace. Он написан на Swift, но может быть применен и к Objective-C. - person Friedrich 'Fred' Clausen; 07.12.2015
comment
Не могли бы вы также написать это для swift? Спасибо - person kishor0011; 09.12.2017
comment
он действительно отлично работает для iOS 11. действительно оценен. - person Faruk; 08.05.2018
comment
deleteBackward() не будет вызван, если вы return false в textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool - person igrrik; 06.02.2019
comment
Для вызываемого метода (textFieldDidDelete) существует нехакерский способ сделать что-то только тогда, когда текстовое представление (в моем случае) уже было пустым? Я использую его для удаления строки в таблице, но только при нажатии backspace в пустом текстовом представлении. Мое хакерское решение добавляет для проверки bool. Таким образом, в первый раз, когда текстовое представление пусто, оно меняет значение bool. В следующий раз удалит. (без bool он удаляется каждый раз, когда текстовое представление пусто) - person aestusLabs; 02.01.2020
comment
Какая ваша любимая благотворительная организация (и это может быть вы)? Это просто сэкономило мне целую работу. - person Michael Rogers; 28.08.2020
comment
@MichaelRogers Ха-ха, я рад, что смог помочь. Моя любимая благотворительная организация - gktw.org (Подари детям мир) ???? - person jacob; 29.08.2020
comment
Я сделал пожертвование на ваше имя. Спасибо еще раз! - person Michael Rogers; 30.08.2020

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

Ознакомьтесь с UITextInput, в частности _ 3_ имеет _ 4_ метод делегата, который всегда вызывается при нажатии клавиши удаления. Если вы делаете что-то простое, вы можете просто создать подкласс UILabel и привести его в соответствие с протоколом UIKeyInput, как это сделано с помощью SimpleTextInput и этот Пример iPhone UIKeyInput. Примечание. UITextInput и его родственники (включая UIKeyInput) доступны только в iOS 3.2 и новее.

person ma11hew28    schedule 09.07.2011
comment
Это правильный ответ. Это не взломано и очень просто с быстрым подклассом UITextField. - person Sam; 04.07.2013
comment
Только что заметил ответ Джейкоба ниже. Он дает подробный пример этого. - person Sam; 05.07.2013
comment
Стоит отметить, что этот ответ не работает в iOS 5. Если текстовое поле пусто, нажатие клавиши Backspace не вызывает этот метод. - person jfeldman; 16.09.2013

Код, подобный следующему:

@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.

person Jeffrey Loo    schedule 24.04.2013
comment
Это должен быть принятый ответ. Чисто и собственно отвечает на вопрос. - person Ryan Romanchuk; 29.04.2013
comment
Он отвечает на вопрос ... Пока вы ориентируетесь на iOS 6.0+. К сожалению, в iOS 5 функция deleteBackward никогда не вызывалась в вашем подклассе. - person BJ Homer; 05.06.2013
comment
Б.Дж. Гомер, 93% устройств работают на iOS 6, поэтому отказ от ориентации на iOS 5, как правило, не представляет большого труда. - person Jonathan.; 24.06.2013
comment
Я рад, что пролистал достаточно, чтобы найти это. 100% правильный способ сделать это сегодня в iOS 7. - person MusiGenesis; 01.03.2014
comment
Я видел так много других ответов на этот вопрос, которые являются просто обходными путями, и вряд ли какой-либо из них связан с ситуацией, когда клавиша Backspace нажата в пустом поле. Однако это идеальный и действительно чистый способ сделать это. - person Christopher Hannah; 06.09.2017

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

import UIKit

// Extend from PinTextFieldDelegate instead of UITextFieldDelegate in your class
protocol PinTextFieldDelegate : 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? PinTextFieldDelegate {
            pinDelegate.didPressBackspace(self)
        }
    }
}
person Ruud Visser    schedule 06.01.2017
comment
Спасибо, могу ли я узнать, что этот метод рекомендован Apple или это взлом. это кажется недокументированным для текстового поля. - person kalpesh jetani; 03.05.2017
comment
Работал у меня с textView. Спасибо, что поделился ;) - person Edouard Barbier; 02.07.2017
comment
Работал для меня, когда текстовое поле пусто, и нажата кнопка возврата. - person Ramakrishna; 31.10.2017
comment
Сработало для меня очень полезно. Спасибо :) - person DarkHorse; 27.02.2021

Я нашел другой способ проще, чем 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. Но его правильная работа;)

person Jerapong Nampetch    schedule 20.02.2012
comment
Для плавания на спине не задан '\ b'. Но если вы внимательно отлаживаете, вы увидите '\ 0'. Итак, я получаю результат 0, то есть два значения равны в методе strcmp. const char * stoke = [текст cStringUsingEncoding: NSASCIIStringEncoding]; const char * backstroke = \ 0; // это равно \ b - ›\ x08; strcmp (плавание на спине, ходьба); - person Yoon Lee; 19.01.2013
comment
Кроме того, по определению strcmp (s1, s2) возвращает 0, если s1 == s2, ›0 s1 лексикографически больше, чем s2, наоборот. - person Yoon Lee; 19.01.2013
comment
Я не понимаю, как это могло работать? @YoonLee: разве вы не говорите, что strcmp либо возвращает -1, 0, or 1? Это было моим пониманием. - person chug2k; 20.09.2014
comment
в текстовом представлении вы всегда обнаруживаете обратное пространство, даже если текста нет, UITextField отличается - person LolaRun; 19.12.2014
comment
Это UITextViewDelegate метод, а не UITextFieldDelegate. - person pkamb; 29.11.2016

Попробуйте delegate

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

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

person Niklas Alvaeus    schedule 16.04.2010
comment
Однако вызывается только тогда, когда поле не пустое. Обратите внимание на исходный вопрос. :) - person Eric Goldberg; 20.06.2012

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

Цель C:

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

Swift:

func keyboardInputShouldDelete(_ textField: UITextField) -> Bool { return true }
person Pritesh    schedule 05.06.2017
comment
Tx - как раз то, что мне нужно. Но где это задокументировано ?? - person Andy Weinstein; 09.03.2020

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

Я ограничивал ввод определенным набором символов, но игнорировал обратные пробелы. Поэтому я попросил его проверить 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);
    }   
 }
person Ben Call    schedule 30.04.2010
comment
Метод ожидает в качестве возвращаемого значения логическое значение, а не строку. - person Pascalius; 13.08.2012

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

#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
person furkan3ayraktar    schedule 20.10.2014
comment
Вы можете написать код для Swift. Я получил подтверждение об ошибке в протоколе uikeyinput - person Raj Aggrawal; 03.08.2016

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

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

yourTextField.delegate = self (ОБЯЗАТЕЛЬНО ТРЕБУЕТСЯ)

Swift:

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

Цель C:

- (BOOL)keyboardInputShouldDelete:(UITextField *)textField { 
    return YES; 
}
person Himanshu padia    schedule 26.07.2017
comment
Это даже не требует меня. Все другие методы делегата вызываются по мере возникновения события. - person Hemang; 21.05.2018
comment
Спасибо, дружище .. Ты справился. :) - person MS.; 21.02.2019
comment
@ McDonal_11 Я думаю, вы забыли установить textfield.delegate = self для текстового поля. - person Himanshu padia; 10.06.2020
comment
Я установил. другие методы делегата UITextField работают нормально. Только этот не работает. Что я скучаю ?? - person McDonal_11; 11.06.2020

Лучшее использование, которое я нашел для обнаружения обратного пространства, - это обнаружение, когда пользователь нажимает обратное пространство в пустом 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
person Sam    schedule 11.07.2013
comment
Чтобы это работало в iOS8 (где есть ошибка, из-за которой этот метод делегата никогда не вызывается), см. Этот ответ: stackoverflow. com / a / 25862878/893101. Подробнее об ошибке ios8: devforums.apple.com/message/1009150#1009150 - person pIkEL; 28.09.2014

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

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

person tracy    schedule 22.10.2012

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

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

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

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

Надеюсь, это поможет.

person Jason Lee    schedule 26.12.2011
comment
То же самое с любым типом клавиатуры. - person ma11hew28; 16.07.2014
comment
как мне подключить этот метод к моему текстовому полю? - person user230910; 27.02.2017

Вместо того, чтобы пытаться заранее сконструировать, что БУДЕТ в текстовом поле, или выяснить, какой специальный символ был введен в метод shouldChangeCharactersInRange, я бы предложил сделать следующее:

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

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

person Aaron    schedule 04.12.2012

Создание подклассов 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
}
person joel.d    schedule 20.05.2015

В файле .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;
}   

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

person Anjali Prasad    schedule 16.03.2018

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

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)
    }
}
person Hemang    schedule 21.05.2018

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

То есть, когда вы переопределяете метод 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...
        }
    }
}
person Tim    schedule 15.01.2019

Комплексный обработчик текстового поля с однозначным номером для Swift 5.1:

  • Предполагая, что у вас есть выходная коллекция текстовых полей (также с подключенными делегатами)

1 шаг

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

final class MyTextField: UITextField {

    weak var myDelegate: MyTextFieldDelegate?

    override func deleteBackward() {
        let wasEmpty = text == nil || text == ""

        // then perform normal behavior
        super.deleteBackward()

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

2 шаг

final class ViewController: UIViewController {

    @IBOutlet private var textFields: [MyTextField]!

    override func viewDidLoad() {
        super.viewDidLoad()
        textFields.forEach {
            $0.delegate = self
            $0.myDelegate = self
        }
    }
}

3 шаг

extension ViewController: UITextFieldDelegate, MyTextFieldDelegate {
    func textFieldHasChanged(with text: String, _ tag: Int, for textField: UITextField) {
        textField.text = text

        if let someTextField = (textFields.filter { $0.tag == tag }).first {
            someTextField.becomeFirstResponder()
        } else {
            view.endEditing(true)
        }
    }

    func textField(_ textField: UITextField, didDeleteBackwardAnd wasEmpty: Bool) {
        // If the user was pressing backward and the value was empty, go to previous textField
        textFieldHasChanged(with: "", textField.tag - 1, for: textField)
    }

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        // Restrict to only digits
        let aSet = NSCharacterSet(charactersIn: "0123456789").inverted
        let compSepByCharInSet = string.components(separatedBy: aSet)
        let numberFiltered = compSepByCharInSet.joined(separator: "")

        guard string == numberFiltered, let text = textField.text else { return false }

        if text.count >= 1 && string.isEmpty {
            // If the user is deleting the value
            textFieldHasChanged(with: "", textField.tag - 1, for: textField)
        } else {
            textFieldHasChanged(with: string, textField.tag + 1, for: textField)
        }

        return false
    }
}
person Latenec    schedule 15.12.2019

Вот мое решение, основанное на идее @andrew:

где-нибудь например в viewDidLoad

        textField.delegate = self
        textField.addTarget(self, action: #selector(valueChanged(_:)), for: .editingDidBegin)

а потом

    @objc func valueChanged(_ textField: UITextField) {
        textField.text = "\u{200B}"
    }

    override func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        textField.text = string
        if string == "" {
            //backpaspace pressed 
        }
person rastislv    schedule 16.12.2019

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

Swift 5.0 или выше

  1. Создайте собственный класс текстового поля, расширяющий UITextField, и переопределите функцию deleteBackward -

    class CustomTextField: UITextField {

     var backButtonPressedInEmptyTextField: (()->())?
    
     override func deleteBackward() {
         super.deleteBackward()
         if let text = self.text, text.count == 0{
             backButtonPressedInEmptyTextField?()
             print("Back space clicked when textfield is empty")
         }
     }
    

    }

  2. Предположим, вы хотите что-то сделать на основе этого в вашем ViewController, MyViewController. Итак, в MyViewController просто сделайте следующее:

    class MyViewController: UIViewController {@IBOutlet weak var sampleTextField: CustomTextField! {didSet {sampleTextField.backButtonPressedInEmptyTextField = backButtonPressed ()}}

     func backButtonPressed(){
         //do whatever you want
     }
    

    }

Я чувствую, что с функцией закрытия или обратного вызова она намного чище.

person Natasha    schedule 24.02.2021

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

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

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

person user1375355    schedule 02.08.2012

Используя метод делегата TextField:

- (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
    }
}
person kalim sayyad    schedule 02.07.2014

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

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    if (textView.text == "" && text == "") {
        print("Backspace on empty text field.")
    }
    return true
}
person michael-k101    schedule 25.07.2020

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

     if (range.length==1)
person Bhumit Mehta    schedule 05.06.2013

In 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, и пустой строковый объект заменяет этот единственный символ».

person joker    schedule 30.03.2013
comment
Этот вопрос касается UITextField, а не UITextView. - person ma11hew28; 16.07.2014

person    schedule
comment
Может быть, небольшое пояснение, куда это поставить? - person Alex Cio; 30.04.2014