Событие изменения текста UITextField

563

Как я могу обнаружить любые изменения текста в textField? Метод делегата shouldChangeCharactersInRangeработает для чего-то, но он не полностью соответствует моим потребностям. Так как, пока это не возвращает ДА, тексты textField не доступны для других методов наблюдателя.

например в мой код calculateAndUpdateTextFieldsне попал обновленный текст, пользователь набрал.

Есть ли у них какой-нибудь способ получить что-то вроде textChangedобработчика событий Java.

- (BOOL)textField:(UITextField *)textField 
            shouldChangeCharactersInRange:(NSRange)range 
            replacementString:(NSString *)string 
{
    if (textField.tag == kTextFieldTagSubtotal 
        || textField.tag == kTextFieldTagSubtotalDecimal
        || textField.tag == kTextFieldTagShipping
        || textField.tag == kTextFieldTagShippingDecimal) 
    {
        [self calculateAndUpdateTextFields];

    }

    return YES;
}
Karim
источник
3
Это хороший пример ответа SO с 1000 голосами, что совершенно неверно . iOS хитрая, изящная и полна гочей.
Толстяк

Ответы:

1056

От правильного способа сделать текстовое изменение uitextfield перезвоните :

Я ловлю символы, отправленные в элемент управления UITextField, примерно так:

// Add a "textFieldDidChange" notification method to the text field control.

В Objective-C:

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

В Свифте:

textField.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged)

Затем в textFieldDidChangeметоде вы можете просмотреть содержимое textField и перезагрузить табличное представление по мере необходимости.

Вы могли бы использовать это и поместить CalcuAndUpdateTextFields в качестве вашего selector.

Дэниэл Дж. Уилсон
источник
18
Это лучшее решение - потому что вы также можете установить это в Nibs или Storyboards, и вам не нужно записывать чрезмерный код
UITextFieldTextDidChangeNotification
3
Единственная проблема. Это не работает для меня вместе с - (BOOL) textField: (UITextField *) textField shouldChangeCharactersInRange: (NSRange) range replaceString: (NSString *) строка
iWheelBuy
4
@iWheelBuy Только когда он возвращается NO, что логично, потому что когда вы возвращаетесь NOиз этого метода, вы в основном говорите, что текст в поле не должен изменяться.
gitaarik
2
Это отлично подходит для редактирования вызванных изменений. Он не улавливает программные изменения , которые происходят с помощью: textField.text = "Some new value";. Есть ли умный способ поймать это?
Бенжон
10
Вы бы подумали с Apple, UITextFieldDelegateчто что-то подобное func textField: UITextField, didChangeText text: Stringбыло бы включено, но ... (бросает на парней в Apple грязный взгляд)
Брэндон
394

Ответ XenElement точен.

Вышесказанное можно сделать и в конструкторе интерфейсов, щелкнув правой кнопкой мыши на UITextField и перетащив событие отправки «Редактирование изменено» на ваш подкласс.

Событие изменения UITextField

Уильям Т.
источник
2
Это может быть легко, но после 10-15 минут поиска я не обнаружил такой возможности, пока случайно не наткнулся на ваш ответ. Есть еще один +1 от меня; Приятно, чтобы как на основе кода, так и на основе решений IB голосовали по таким вопросам.
Марк Амери
Я только что проверил это в 6.3, и это там. Это UITextView и щелкните по нему правой кнопкой мыши и увидите «Редактирование изменено».
Уильям Т.
4
с какой стати это называется Editing Changed ?? без ума! спасибо за решение, хотя :-)
Малхал
3
Поскольку событие вызывается при редактировании изменений, тогда как значение «Изменено» происходит, когда текстовое поле заканчивает редактирование, т.е. уходит в отставку первый респондент. UITextFieldTextDidChangeNotification является уведомлением UITextField, в то время как UIControlEventValueChanged реализуется UIControl, подклассы которого UIButton.
Camsoft
2
Я замечаю, что это не работает, поскольку пользовательские вкладки выходят за пределы текстового поля, а текст с
автозаменой
136

установить слушателя события:

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

на самом деле слушать:

- (void)textFieldDidChange:(UITextField *)textField {
    NSLog(@"text changed: %@", textField.text);
}
ASDF
источник
прохладно. Мне нравится этот подход, потому что я хотел бы реализовать textField-change-listening в базовом классе ViewController, который уже является TextFieldDelegate. Таким образом, я могу добавить Target: baseControllerMethod для (textField в подпредставлениях) как очарование. СПАСИБО!
HBublitz
87

Swift:

yourTextfield.addTarget(self, action: #selector(textFieldDidChange(textField:)), for: .editingChanged)

Затем реализуйте функцию обратного вызова:

@objc func textFieldDidChange(textField: UITextField){

print("Text changed")

}
Хуан Боэро
источник
2
Работая для меня, я уже пробовал это, но не понимал, что функция нуждается в аргументе textField. Если это не работает для вас, убедитесь, что у вашего селектора есть аргумент для передачи textField (даже если вы его не используете).
werm098
Добавьте _ перед textField в textFieldDidChange следующим образом: func textFieldDidChange (textField: UITextField) {...}
Makalele
30

Как указано здесь: событие изменения текста UITextField , похоже, что в iOS 6 (iOS 6.0 и 6.1 проверено) невозможно полностью обнаружить изменения в UITextFieldобъектах, просто наблюдая UITextFieldTextDidChangeNotification.

Кажется, что теперь отслеживаются только те изменения, которые были сделаны непосредственно встроенной клавиатурой iOS. Это означает, что если вы измените свой UITextFieldобъект, просто вызвав что-то вроде этого:, myUITextField.text = @"any_text"вы не будете уведомлены о каких-либо изменениях вообще.

Я не знаю, если это ошибка или она предназначена. Похоже, ошибка, так как я не нашел разумного объяснения в документации. Это также указано здесь: UITextField событие изменения текста .

Мое «решение» этого заключается в том, чтобы фактически публиковать уведомление самостоятельно для каждого изменения, которое я делаю в моем UITextField(если это изменение сделано без использования встроенной клавиатуры iOS). Что-то вроде этого:

myUITextField.text = @"I'm_updating_my_UITextField_directly_in_code";

NSNotification *myTextFieldUpdateNotification  = 
  [NSNotification notificationWithName:UITextFieldTextDidChangeNotification
                  object:myUITextField];

[NSNotificationCenter.defaultCenter 
  postNotification:myTextFieldUpdateNotification];

Таким образом, вы на 100% уверены, что получите такое же уведомление при изменении .textсвойства вашего UITextFieldобъекта, либо когда вы обновите его «вручную» в своем коде, либо через встроенную клавиатуру iOS.

Важно учитывать, что, поскольку это не задокументированное поведение, такой подход может привести к получению 2 уведомлений об одном и том же изменении вашего UITextFieldобъекта. В зависимости от ваших потребностей (что вы на самом деле делаете, когда ваши UITextField.textизменения) это может быть неудобно для вас.

Немного другой подход заключается в публикации пользовательского уведомления (то есть с другим именем, отличным от UITextFieldTextDidChangeNotification), если вам действительно нужно знать, было ли это уведомление вашим или «созданным на iOS».

РЕДАКТИРОВАТЬ:

Я только что нашел другой подход, который, я думаю, мог бы быть лучше:

Это включает функцию наблюдения значения ключа (KVO) в Objective-C ( http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/KeyValueObserving/KeyValueObserving.html#//apple_ref/doc/uid / 10000177-BCICJDHA ).

По сути, вы регистрируетесь как наблюдатель собственности, и если это свойство изменяется, вы получаете уведомление об этом. «Принцип» очень похож на то, как NSNotificationCenterработает, и является главным преимуществом, что этот подход работает автоматически, также как и в iOS 6 (без какой-либо специальной настройки, такой как необходимость вручную отправлять уведомления).

Для нашего UITextFieldсценария это прекрасно работает, если вы добавите этот код, например, в UIViewControllerтот , который содержит текстовое поле:

static void *myContext = &myContext;

- (void)viewDidLoad {
  [super viewDidLoad];

  //Observing changes to myUITextField.text:
  [myUITextField addObserver:self forKeyPath:@"text"
    options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld 
    context:myContext];

}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object 
change:(NSDictionary *)change context:(void *)context {

  if(context == myContext) {
    //Here you get notified every time myUITextField's "text" property is updated
    NSLog(@"New value: %@ - Old value: %@",
      [change objectForKey:NSKeyValueChangeNewKey],
      [change objectForKey:NSKeyValueChangeOldKey]);
  }
  else 
    [super observeValueForKeyPath:keyPath ofObject:object 
      change:change context:context];

}

Отдайте должное этому ответу об управлении контекстом: https://stackoverflow.com/a/12097161/2078512

Примечание. Похоже, что во время редактирования UITextFieldс помощью встроенной клавиатуры iOS свойство «текст» текстового поля не обновляется при вводе / удалении каждой новой буквы. Вместо этого объект текстового поля обновляется «в целом» после того, как вы отказываетесь от статуса первого респондента текстового поля.

ercolemtar
источник
5
Зачем испытывать себя, просто используйте метод target-action из ответа XenElement. Если вам нужны десятки строк кода, делайте то, что «должно» быть простым, вы, вероятно, делаете это неправильно.
Jrturton
6
Казалось бы, предполагаемое поведение. Никакие другие методы делегата (например, прокрутка, выборка) не вызываются для событий, инициируемых кодом.
jrturton
1
Обратите внимание, что UIKit не гарантирует совместимость с KVO , поэтому решение KVO не обязательно будет работать.
Пан
@jrturton, в отношении вашего вопроса «почему», совершенно обычным явлением является то, что в каком-то другом классе (например, в оформлении, анимации и т. п.) вам нужно реагировать на то, что происходит в текстовом поле.
Толстяк
27

Мы можем легко настроить это Storyboard, CTRL перетащить @IBActionсобытие и изменить следующим образом:

введите описание изображения здесь

бикрам сапкота
источник
14

Здесь в быстрой версии для того же.

textField.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)

func textFieldDidChange(textField: UITextField) {

}

Спасибо

индус
источник
12

Я решил проблему, изменив поведение shouldChangeChractersInRange. Если вы вернете НЕТ, изменения не будут применены iOS внутренне, вместо этого у вас есть возможность изменить его вручную и выполнить любые действия после изменений.

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    //Replace the string manually in the textbox
    textField.text = [textField.text stringByReplacingCharactersInRange:range withString:string];
    //perform any logic here now that you are sure the textbox text has changed
    [self didChangeTextInTextField:textField];
    return NO; //this make iOS not to perform any action
}
Паулс
источник
3
Проблема с этим решением: позиция курсора устанавливается на конец поля (как только вы сделаете textField.text = ...). Таким образом, если пользователь хочет редактировать символы в середине поля, то при каждом нажатии клавиши его курсор будет перемещаться до конца поля. Попробуйте и посмотрите.
FranticRock
Хороший вопрос @ Алекс, но легко обойти. Просто вычислите результирующее значение в локальной NSString и передайте его в didChangeTextInTextField: toText: метод, затем верните YES, чтобы позволить iOS выполнить обновление.
jk7
11

Версия Swift протестирована:

//Somewhere in your UIViewController, like viewDidLoad(){ ... }
self.textField.addTarget(
        self, 
        action: #selector(SearchViewController.textFieldDidChange(_:)),
        forControlEvents: UIControlEvents.EditingChanged
)

Параметры объяснили:

self.textField //-> A UITextField defined somewhere in your UIViewController
self //-> UIViewController
.textFieldDidChange(_:) //-> Can be named anyway you like, as long as it is defined in your UIViewController

Затем добавьте метод, который вы создали выше в вашем UIViewController:

//Gets called everytime the text changes in the textfield.
func textFieldDidChange(textField: UITextField){

    print("Text changed: " + textField.text!)

}
Kemicofa Ghost
источник
7

Swift 4

func addNotificationObservers() {

    NotificationCenter.default.addObserver(self, selector: #selector(textFieldDidChangeAction(_:)), name: .UITextFieldTextDidChange, object: nil)

}

@objc func textFieldDidChangeAction(_ notification: NSNotification) {

    let textField = notification.object as! UITextField
    print(textField.text!)

}
acidgate
источник
5

Для Swift 3.0:

let textField = UITextField()

textField.addTarget(
    nil,
    action: #selector(MyClass.textChanged(_:)),
    for: UIControlEvents.editingChanged
)

используя класс как:

class MyClass {
    func textChanged(sender: Any!) {

    }
}
pedrouan
источник
4

Версия Swift 3

yourTextField.addTarget(self, action: #selector(YourControllerName.textChanges(_:)), for: UIControlEvents.editingChanged)

И получить изменения здесь

func textChanges(_ textField: UITextField) {
    let text = textField.text! // your desired text here
    // Now do whatever you want.
}

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

Риаджур Рахман
источник
2

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

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textFiledEditChanged:)
                                                 name:@"UITextFieldTextDidChangeNotification"
                                               object:youTarget];

тогда

- (void)textFiledEditChanged:(NSNotification *)obj {
UITextField *textField = (UITextField *)obj.object;
NSString *toBestring = textField.text;
NSArray *currentar = [UITextInputMode activeInputModes];
UITextInputMode *currentMode = [currentar firstObject];
if ([currentMode.primaryLanguage isEqualToString:@"zh-Hans"]) {
    UITextRange *selectedRange = [textField markedTextRange];
    UITextPosition *position = [textField positionFromPosition:selectedRange.start offset:0];
    if (!position) {
        if (toBestring.length > kMaxLength)
            textField.text =  toBestring;
} 

}

наконец, вы бежите, будет сделано.

HsuChihYung
источник
2

Swift 3.1:

Селектор: ClassName.MethodName

  cell.lblItem.addTarget(self, action: #selector(NewListScreen.fieldChanged(textfieldChange:)), for: .editingChanged)

  func fieldChanged(textfieldChange: UITextField){

    }
Beyaz
источник
2

Версия Swift 3:

class SomeClass: UIViewController, UITextFieldDelegate { 

   @IBOutlet weak var aTextField: UITextField!


    override func viewDidLoad() {
        super.viewDidLoad()

        aTextField.delegate = self
        aTextField.addTarget(self, action: #selector(SignUpVC.textFieldDidChange), for: UIControlEvents.editingChanged)        
    }

   func textFieldDidChange(_ textField: UITextField) {

       //TEXT FIELD CHANGED..SECRET STUFF

   }

}

Не забудьте установить делегата.

Иосиф Фрэнсис
источник
У меня есть 6 текстовых полей в моем контроллере представления, и я просто хочу проверить, было ли изменено последнее текстовое поле. Print («последнее текстовое поле было изменено!»). Вы можете помочь?
Саид Рахматолахи
Сначала сделайте Iboutlet для вашего textField. Затем установите делегата и addTarget, как я показал в своем ответе. В функции делегата textFieldDidChange добавьте следующее: «if textField == yourLastTextField {print (« последнее текстовое поле было изменено! »)} Сейчас у меня нет доступа к компьютеру. Я сделаю этот комментарий более читабельным, когда я добраться до моего компьютера
Джозеф Фрэнсис
2

С закрытием:

   class TextFieldWithClosure: UITextField {
    var targetAction: (() -> Void)? {
        didSet {
            self.addTarget(self, action: #selector(self.targetSelector), for: .editingChanged)
        }
    }

    func targetSelector() {
        self.targetAction?()
    }
    }

и используя

textField.targetAction? = {
 // will fire on text changed
 }
ebohdas
источник
2
  1. КВО решения не работают

KVO НЕ работает в iOS для элементов управления: http://stackoverflow.com/a/6352525/1402846 https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/KVO.html

  1. В классе наблюдения вы делаете это:

Учитывая, что вы знаете текстовое представление, которое хотите посмотреть:

var watchedTextView: UITextView!

Сделай это:

NotificationCenter.default.addObserver(
    self,
    selector: #selector(changed),
    name: UITextView.textDidChangeNotification,
    object: watchedTextView)

Однако будьте осторожны с этим:

  • скорее всего, вы хотите позвонить только один раз , поэтому не вызывайте его, например,layoutSubviews

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

  • например, вы обычно наверняка не можете позвонить initвовремя, поскольку, конечно, еще watchedTextViewне может существовать

,

  1. следующая проблема в том, что ...

Ни одно из уведомлений не вызывается, когда текст изменяется программно .

Это огромное, давнее и глупое неудобство в разработке iOS.

Элементы управления просто не - конец истории - вызывают уведомления, когда свойство .text изменяется программно.

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

Вы должны создать подкласс текстового представления (или аналогичного элемента управления) следующим образом:

class NonIdioticTextView: UIITextView {

    override var text: String! {
        // boilerplate code needed to make watchers work properly:
        get {
            return super.text
        }
        set {
            super.text = newValue
            NotificationCenter.default.post(
                name: UITextView.textDidChangeNotification,
                object: self)
        }

    }

}

(Совет - не забывайте, что супер звонок должен прийти раньше ! После звонка.)

Там является не доступным решением, если вы не исправить управление по подклассам , как показано чуть выше. Это единственное решение.

Обратите внимание, что уведомление

UITextView.textDidChangeNotification

результаты в

func textViewDidChangeSelection(_ textView: UITextView) 

будучи призванным

( Не textViewDidChange .)

Fattie
источник
1
[[NSNotificationCenter defaultCenter] addObserver:self 
selector:@selector(didChangeTextViewText:) 
name:UITextFieldTextDidChangeNotification object:nil];



- (void) didChangeTextViewText {
 //do something
}
PeiweiChen
источник
0

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

private func setupTextFieldNotification() {
    NotificationCenter.default.addObserver(forName: UITextField.textDidChangeNotification, object: nil, queue: OperationQueue.main) { (notification) in
        if let textField = notification.object as? UITextField, textField.tag == 100, let text = textField.text {
            print(#line, text)
        }
    }
}

deinit {
    NotificationCenter.default.removeObserver(self)
}
smileBot
источник
-1

Версия Swift 4

Использование наблюдения значения ключа Оповещайте объекты об изменениях свойств других объектов.

var textFieldObserver: NSKeyValueObservation?

textFieldObserver = yourTextField.observe(\.text, options: [.new, .old]) { [weak self] (object, changeValue) in
  guard let strongSelf = self else { return }
  print(changeValue)
}
Валерий
источник
Хорошая идея. Вы можете обернуть это в контекст и сообщить нам?
Ревант Каусикан
1
Это, к сожалению, совершенно неправильно. KVO НЕ работает в iOS для элементов управления: stackoverflow.com/a/6352525/1402846 developer.apple.com/library/archive/documentation/General/…
Толстяк