UITextFieldテキスト変更イベント


563

textFieldでテキストの変更を検出するにはどうすればよいですか?デリゲートメソッドshouldChangeCharactersInRangeは何かに対して機能しますが、私のニーズを正確に満たしませんでした。YESが返されるまで、textFieldテキストは他のオブザーバーメソッドでは使用できません。

たとえば、私のコードでcalculateAndUpdateTextFieldsは、更新されたテキストを取得できませんでした。ユーザーが入力しました。

textChangedJavaイベントハンドラーのようなものを取得する方法はありますか。

- (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票のSO回答の良い例ですが、これは完全に正しくありません。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];

Swiftの場合:

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

次に、textFieldDidChangeメソッドでtextFieldの内容を調べ、必要に応じてテーブルビューを再読み込みします。

これを使用して、calculateAndUpdateTextFieldsをとして配置できますselector


18
これはより良いソリューションです-ニブまたはストーリーボードでこれを設定することもでき、UITextFieldTextDidChangeNotificationコードを過度に書き出す必要がないため
PostCodeism '27 / 02/27

3
唯一の問題です。-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
iWheelBuy

4
@iWheelBuyが返される場合のみNO、これは論理的NOです。これは、このメソッドから戻ると、基本的にはフィールドのテキストは変更されないということです。
gitaarik 2014年

2
これは、編集によって引き起こされた変更に最適です。次を介して発生するプログラムの変更はキャッチされませんtextField.text = "Some new value";。これをキャッチする賢い方法はありますか?
Benjohn

10
あなたはApple UITextFieldDelegateと似たようなものfunc textField: UITextField, didChangeText text: Stringが含まれているだろうと思ったでしょうが、... (Appleの人を汚く見せます)
Brandon A

394

XenElementの答えはその場にあります。

上記は、UITextFieldを右クリックし、「Editing Changed」送信イベントをサブクラスユニットにドラッグすることで、インターフェイスビルダーでも実行できます。

UITextField Changeイベント


2
簡単かもしれませんが、10〜15分の検索の後、私は偶然あなたの答えに出くわすまでこの可能性を発見しませんでした。私から別の+1をもらいます。コードベースのソリューションとIBベースのソリューションの両方でこのような質問に高い投票ができるのは素晴らしいことです。
マークアメリー2013

6.3でチェックしたところです。これはUITextViewであり、それを右クリックすると「編集が変更されました」が表示されます。
ウィリアムT.

4
なぜそれが編集変更と呼ばれるのですか?狂った!しかし解決策をありがとう:-)
malhal

3
変更の編集時にイベントが呼び出されるのに対し、テキストフィールドの編集が終了したとき、つまり最初のレスポンダを辞任したときに、値の変更が発生します。UITextFieldTextDidChangeNotificationはUITextField通知ですが、UIControlEventValueChangedは、UIButtonのサブクラスであるUIControlによって実装されます。
Camsoft

2
ユーザーがテキストフィールドからタブで移動し、
オート

136

イベントリスナーを設定するには:

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

実際に聞く:

- (void)textFieldDidChange:(UITextField *)textField {
    NSLog(@"text changed: %@", textField.text);
}

涼しい。すでにTextFieldDelegateであるViewController基本クラスにtextField-change-listeningを実装したいので、このアプローチが好きです。このようにして、チャームのように(サブビューのtextField)にTargetTarget:baseControllerMethodを追加できます。THX!
HBublitz 2014

87

迅速:

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

次に、コールバック関数を実装します。

@objc func textFieldDidChange(textField: UITextField){

print("Text changed")

}

2
私のために働きました、私はすでにこれを試しましたが、関数がtextField引数を必要とすることに気づきませんでした。これがうまくいかない場合は、セレクターに渡されるtextFieldの引数があることを確認してください(使用しない場合でも)。
werm098 2016年

次のように、textFieldDidChangeのtextFieldの前に_を追加します。functextFieldDidChange(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];

このようにして、コードで「手動」で更新したとき、または組み込みのiOSキーボード.textを使用してUITextFieldオブジェクトのプロパティを変更したときにも、同じ通知を受け取ることを100%確信できます。

これは文書化された動作ではないため、このアプローチでは、UITextFieldオブジェクトの同じ変更に対して2つの通知が受信される可能性があることを考慮することが重要です。必要に応じて(UITextField.text変更時に実際に行うこと)、これは不便な場合があります。

UITextFieldTextDidChangeNotification通知が自分のものであるか「iOS製」であるかを実際に知る必要がある場合は、少し異なるアプローチとして、カスタム通知(これは以外のカスタム名を使用)を投稿します。

編集:

私はちょうど良いかもしれないと思う別のアプローチを見つけました:

これには、Objective-C のキー値監視(KVO)機能(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

注:組み込みのiOSキーボードで編集しUITextFieldている間、テキストフィールドの「text」プロパティは、新しい文字がすべて入力/削除されても更新されないようです。代わりに、テキストフィールドのファーストレスポンダステータスを辞任すると、テキストフィールドオブジェクトが「全体として」更新されます。


5
なぜこれを経験するのか、XenElementの回答からtarget-actionメソッドを使用するだけです。数十行のコードが単純であるはずの何かをする必要がある場合、おそらくそれは間違っています。
jrturton 2013

6
これは意図された動作のようです。他のデリゲートメソッド(スクロール、選択など)は、コードによって発生したイベントに対して呼び出されません。
jrturton 2013

1
注意のUIKitはKVO準拠であることが保証されていないので、KVOソリューション意志とは限らない作品。
2013

@jrturton、「なぜ」の質問に関しては、他のクラス(おそらく装飾やアニメーションなど)では、テキストフィールドで起こっていることに応答する必要があることは完全に当​​たり前のことです。
Fattie

27

これをから簡単に構成できStoryboard、CTRLを押し@IBActionて次のようにイベントを変更します。

ここに画像の説明を入力してください


14

ここで同じの迅速なバージョンで。

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

func textFieldDidChange(textField: UITextField) {

}

ありがとう


12

shouldChangeChractersInRangeの動作を変更する問題を解決しました。NOを返すと、変更は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
このソリューションの問題は、カーソル位置がフィールドのENDに設定されることです(textField.text = ...を実行するとすぐ)。したがって、ユーザーがフィールドの中央で文字を編集したい場合は、キーを押すたびに、カーソルがフィールドの最後に移動します。試してみてください。
FranticRock 2015

良い点@Alexですが、簡単に回避できます。ローカルの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

特に中国語の入力方法を使用する場合は、他の方法が実際の入力ではなく入力ボックスをリッスンするため、この問題を解決するには通知を使用する必要があります。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;
} 

}

最後に、実行します。


2

Swift 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つのテキストフィールドがあり、最後のテキストフィールドが変更されたかどうかを確認したいだけですprint( "最後のテキストフィールドが変更されました!")
Saeed Rahmatolahi 2017年

まず、textFieldのIboutletを作成します。次に、回答で示したようにデリゲートとaddTargetを設定します。デリゲート関数textFieldDidChangeで、これを追加します: "if textField == yourLastTextField {print(" last Text Field has Changed! ")}現在、自分のコンピューターにアクセスできません。このコメントを読みやすくするために、私のコンピューターに
ジョセフフランシス

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

  • 育児プロセスの最中にいつ呼び出すのが最適かを知るのは非常に困難です。それはあなたの状況に依存します。残念ながら、標準のロックインソリューションはありません

  • たとえば、もちろんまだ存在していない可能性があるため、通常は確かに時間に呼び出すことはできませinitwatchedTextView

  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

1つは、複数のUITextFieldがある可能性があることです。したがって、タグを付ければ、タグをオンにすることができます。ここでは、どのクラスでもほぼオブザーバーを設定する方法を説明します。

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バージョン

Key-Value Observing Notifyオブジェクトを使用して、他のオブジェクトのプロパティの変更について通知します。

var textFieldObserver: NSKeyValueObservation?

textFieldObserver = yourTextField.observe(\.text, options: [.new, .old]) { [weak self] (object, changeValue) in
  guard let strongSelf = self else { return }
  print(changeValue)
}

良いアイデア。これをコンテキストにラップしてお知らせください。
Revanth Kausikan、

1
残念ながら、これは完全に間違っています。KVOは、コントロールのためのiOSでは動作しません: stackoverflow.com/a/6352525/1402846 developer.apple.com/library/archive/documentation/General/...
Fattie
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.