Подія зміни тексту UITextField


563

Як я можу виявити будь-які зміни в текстовому полі? Метод делегата 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;
}

3
Це хороший приклад відповіді на відповідь на 1000 голосів, що абсолютно невірно . iOS хитрий, приємний та насичений.
Fattie

Відповіді:


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 та перезавантажити подання таблиці за потребою.

Ви можете використати це і поставити калькуляторAndUpdateTextFields як своє selector.


18
Це найкраще рішення - бо ви можете також встановити це в розкадровки або пір'ї, і вам не доведеться виписувати зайвий код UITextFieldTextDidChangeNotification
PostCodeism

3
Єдине питання. Для мене це не працює разом із - (BOOL) textField: (UITextField *) textField shouldChangeCharactersInRange: (NSRange) діапазон заміниString: (NSString *) рядок
iWheelBuy

4
@iWheelBuy Тільки коли він повертається NO, що логічно, тому що, повертаючись NOіз цього методу, ви в основному говорите, що текст у полі не повинен змінюватися.
gitaarik

2
Це чудово підходить для редагування викликаних змін. Він не вловлює програмні зміни , які відбуваються з допомогою: textField.text = "Some new value";. Чи є розумний спосіб це зрозуміти?
Benjohn

10
Ви б могли подумати з Apple, UITextFieldDelegateщо щось подібне func textField: UITextField, didChangeText text: Stringбуло б включене, але ... (надає хлопцям в Apple брудний вигляд)
Брендон A

394

Відповідь XenElement - це місце.

Сказане може бути зроблено і в програмі інтерфейсів, клацнувши правою кнопкою миші на полі UITextField та перетягуючи посилання відправки "Редагування змінено" на підрозділ підкласу.

Подія зміни UITextField


2
Це може бути просто, але через 10-15 хвилин пошуку я не виявив такої можливості, поки випадково натрапив на вашу відповідь. Майте ще один +1 від мене; приємно, щоб як рішення на основі коду, так і на базі ІБ голосували на таких питаннях.
Марк Амері

Я щойно перевірив це в 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);
}

круто. Мені подобається такий підхід, тому що я хотів би реалізувати прослуховування textField-прослуховування в базовому класі ViewController, який вже є TextFieldDelegate. Таким чином я можу додатиTarget: baseControllerMethod for (textField у підпоглядах) як шарм. ДЯКУЮ!
HBublitz

87

Швидкий:

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

Потім реалізуйте функцію зворотного виклику:

@objc func textFieldDidChange(textField: UITextField){

print("Text changed")

}

2
Я працював для мене, я вже пробував це, але не усвідомлював, що потрібна аргумент 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.

Важливо врахувати, що оскільки це не документально підтверджена поведінка, такий підхід може призвести до отримання двох повідомлень про однакову зміну вашого UITextFieldоб’єкта. Залежно від ваших потреб (що ви насправді робите під час UITextField.textзмін), це може бути незручністю для вас.

Трохи іншим підходом буде розміщення спеціального сповіщення (це власне ім'я, відмінне від UITextFieldTextDidChangeNotification), якщо вам потрібно знати, чи було сповіщення вашим чи "iOS-made".

Редагувати:

Щойно я знайшов інший підхід, який, на мою думку, міг би бути кращим:

Сюди входить особливість спостереження ключових значень (KVO) об'єктива-C ( http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/KeyValueObserving/KeyValueObserving.html#//apple_ref/doc/uid / 10000177-BCICJDHA ).

В основному ви реєструєте себе як спостерігач власності, і якщо ця власність зміниться, ви отримаєте сповіщення про неї. "Принцип" досить схожий на те, як NSNotificationCenterпрацює, головна перевага в тому, що цей підхід працює автоматично також, як і в iOS 6 (без будь-яких спеціальних налаштувань, як, наприклад, вручну розміщувати повідомлення).

Для нашого UITextField-scenario це працює чудово, якщо ви додасте цей код, наприклад, до вашого, 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 клавіатури, властивість «текст» текстового поля не оновлюється з кожним новим листом , набраними / видалено. Натомість об’єкт текстового поля оновлюється "в цілому" після відставки статусу першого текстового поля першого відповіді.


5
Навіщо переживати це, просто використовуйте метод цільової дії з відповіді XenElement. Якщо вам потрібно десятки рядків коду зробити щось, що "повинно" бути простим, ви, ймовірно, робите це неправильно.
jrturton

6
Це, здавалося б, призначена поведінка. Жодні інші способи делегування (наприклад, прокрутка, вибір) не вимагають подій, породжених кодом.
jrturton

1
Зауважте, що UIKit не гарантовано відповідає KVO , тому рішення KVO не обов'язково працює.
Панг

@jrturton, стосовно вашого "чому" питання, цілком звично, що в якомусь іншому класі (можливо, прикрасі, анімації чи тому подібному) вам потрібно відповісти на те, що відбувається в текстовому полі.
Fattie


14

Тут у швидкій версії для того ж.

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

func textFieldDidChange(textField: UITextField) {

}

Дякую


12

Я вирішив проблему, змінивши поведінку в системіChangeChractersInRange. Якщо ви повернете НІ, зміни 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!)

}

7

Швидкий 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!)

}

5

Для Swift 3.0:

let textField = UITextField()

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

використовуючи клас типу:

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

    }
}

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

Ви повинні використовувати сповіщення для вирішення цієї проблеми, оскільки інший метод прослухає поле введення, а не власне введення, особливо коли ви використовуєте китайський метод введення. З огляду завантаження

[[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;
} 

}

нарешті, ти біжиш, буде зроблено.


2

Швидкий 3.1:

Вибір: ClassName.MethodName

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

  func fieldChanged(textfieldChange: UITextField){

    }

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 текстових полів, і я просто хочу перевірити, чи було змінено останнє текстове поле друку ("останнє текстове поле було змінено!") Ви можете допомогти?
Саїд Рахматолахі

Спочатку зробіть Iboutlet для свого текстового поля. Потім встановіть делегата та addTarget, як я показав у своїй відповіді. У функції делегування textFieldDidChange додайте це: "якщо 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
 }

2
  1. Рішення KVO не працюють

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


1
[[NSNotificationCenter defaultCenter] addObserver:self 
selector:@selector(didChangeTextViewText:) 
name:UITextFieldTextDidChangeNotification object:nil];



- (void) didChangeTextViewText {
 //do something
}

0

Одне те, що у вас може бути кілька полів UITextFi. Отже, дайте їм тег, і тоді ви можете вмикати теги. Ось як можна налаштувати спостерігача в будь-який клас.

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

-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/…
Fattie
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.