Acara perubahan teks UITextField

563

Bagaimana saya bisa mendeteksi perubahan teks di TextField? Metode delegasi shouldChangeCharactersInRangeberfungsi untuk sesuatu, tetapi itu tidak memenuhi kebutuhan saya persis. Sejak sampai mengembalikan YA, teks textField tidak tersedia untuk metode pengamat lainnya.

misalnya dalam kode saya calculateAndUpdateTextFieldstidak mendapatkan teks yang diperbarui, pengguna telah mengetik.

Apakah cara mereka untuk mendapatkan sesuatu seperti textChangedJava event handler.

- (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
sumber
3
Ini adalah contoh jawaban SO yang bagus dengan 1000 suara yang sama sekali tidak benar . iOS rumit, suibtle, dan penuh dengan gotcha.
Fattie

Jawaban:

1056

Dari cara yang tepat untuk melakukan perubahan teks uitextfield, panggil kembali :

Saya menangkap karakter yang dikirim ke kontrol UITextField sesuatu seperti ini:

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

Dalam Objective-C:

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

Dalam Swift:

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

Kemudian dalam textFieldDidChangemetode ini Anda dapat memeriksa konten textField, dan memuat kembali tampilan tabel Anda sesuai kebutuhan.

Anda bisa menggunakan itu dan menempatkan calculAndUpdateTextFields sebagai milik Anda selector.

Daniel G. Wilson
sumber
18
Ini adalah solusi yang lebih baik - karena Anda juga dapat mengatur ini di Nibs atau Storyboards, dan Anda tidak perlu menuliskan UITextFieldTextDidChangeNotification code yang berlebihan
PostCodeism
3
Satu-satunya masalah adalah. Ini tidak berfungsi untuk saya bersama - - (BOOL) textField: (UITextField *) textField harusUbahCharactersInRange: (NSRange) range replacement replacementString: (NSString *) string
iWheelBuy
4
@ iWheelBuy Hanya ketika kembali NO, yang logis, karena ketika Anda kembali NOdari metode ini, Anda pada dasarnya mengatakan teks di lapangan tidak boleh berubah.
gitaarik
2
Ini bagus untuk mengedit perubahan yang disebabkan. Itu tidak menangkap perubahan program yang terjadi melalui: textField.text = "Some new value";. Apakah ada cara pintar untuk menangkap ini?
Benjohn
10
Anda akan berpikir dengan Apple UITextFieldDelegatebahwa sesuatu seperti func textField: UITextField, didChangeText text: Stringakan dimasukkan, tetapi ... (memberi orang-orang di Apple tampilan yang kotor)
Brandon A
394

Jawaban XenElement tepat.

Hal di atas dapat dilakukan dalam pembangun antarmuka juga dengan mengklik kanan pada UITextField dan menyeret acara pengiriman "Editing yang Diubah" ke unit subkelas Anda.

UITextField Change Event

William T.
sumber
2
Mungkin mudah, tetapi setelah 10-15 menit mencari, saya belum menemukan kemungkinan ini sampai menemukan jawaban Anda secara kebetulan. Dapatkan +1 lainnya dari saya; senang memiliki solusi berbasis kode dan berbasis IB yang sangat dipilih untuk pertanyaan seperti ini.
Mark Amery
Saya baru saja memeriksanya di 6.3 dan ada di sana. Ini adalah UITextView dan klik kanan padanya dan lihat "Editing Diubah".
William T.
4
kenapa sih namanya Editing Changed ?? gila! terima kasih atas solusinya :-)
malhal
3
Karena acara ini disebut ketika mengedit perubahan, sedangkan Nilai Berubah terjadi ketika bidang teks selesai mengedit yaitu mengundurkan diri responden pertama. UITextFieldTextDidChangeNotification adalah notifikasi UITextField, sementara UIControlEventValueChanged diimplementasikan oleh UIControl yang merupakan subclass UIButton.
Camsoft
2
Saya perhatikan ini tidak berfungsi karena tab pengguna keluar dari bidang teks dan teks
koreksi
136

untuk mengatur pendengar acara:

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

untuk benar-benar mendengarkan:

- (void)textFieldDidChange:(UITextField *)textField {
    NSLog(@"text changed: %@", textField.text);
}
asdf
sumber
keren. Saya suka pendekatan ini, karena saya ingin mengimplementasikan textField-change-listening dalam kelas dasar ViewController yang sudah menjadi TextFieldDelegate. Dengan cara ini saya dapat menambahkanTarget: baseControllerMethod untuk (textField in subviews) seperti pesona. TERIMA KASIH!
HBublitz
87

Cepat:

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

Kemudian, terapkan fungsi panggilan balik:

@objc func textFieldDidChange(textField: UITextField){

print("Text changed")

}
Juan Boero
sumber
2
Bekerja untuk saya, saya sudah mencoba ini tetapi tidak menyadari bahwa fungsi tersebut membutuhkan argumen textField. Jika ini tidak berhasil untuk Anda, pastikan untuk memeriksa bahwa pemilih Anda memiliki argumen untuk textField yang akan diteruskan (bahkan jika Anda tidak menggunakannya).
werm098
Tambahkan _ sebelum textField di textFieldDidChange seperti ini: func textFieldDidChange (textField: UITextField) {...}
Makalele
30

Seperti yang dinyatakan di sini: Peristiwa perubahan teks UITextField , tampaknya pada iOS 6 (iOS 6.0 dan 6.1 dicentang) tidak mungkin untuk sepenuhnya mendeteksi perubahan UITextFieldobjek hanya dengan mengamati UITextFieldTextDidChangeNotification.

Tampaknya hanya perubahan itu yang dilakukan secara langsung oleh keyboard iOS bawaan yang dilacak sekarang. Ini berarti bahwa jika Anda mengubah UITextFieldobjek Anda hanya dengan memanggil sesuatu seperti ini:, myUITextField.text = @"any_text"Anda tidak akan diberitahu tentang perubahan sama sekali.

Saya tidak tahu apakah ini bug atau memang dimaksudkan. Sepertinya bug karena saya belum menemukan penjelasan yang masuk akal dalam dokumentasi. Ini juga dinyatakan di sini: Acara perubahan teks UITextField .

"Solusi" saya untuk ini adalah mengirimkan pemberitahuan sendiri untuk setiap perubahan yang saya buat UITextField(jika perubahan itu dilakukan tanpa menggunakan keyboard iOS bawaan). Sesuatu seperti ini:

myUITextField.text = @"I'm_updating_my_UITextField_directly_in_code";

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

[NSNotificationCenter.defaultCenter 
  postNotification:myTextFieldUpdateNotification];

Dengan cara ini Anda 100% yakin bahwa Anda akan menerima pemberitahuan yang sama ketika Anda mengubah .textproperti UITextFieldobjek Anda , baik ketika Anda memperbarui "secara manual" dalam kode Anda atau melalui keyboard iOS bawaan.

Penting untuk mempertimbangkan bahwa, karena ini bukan perilaku yang terdokumentasi, pendekatan ini dapat menyebabkan 2 pemberitahuan diterima untuk perubahan yang sama pada UITextFieldobjek Anda . Tergantung pada kebutuhan Anda (apa yang sebenarnya Anda lakukan saat UITextField.textperubahan), ini bisa menjadi ketidaknyamanan bagi Anda.

Pendekatan yang sedikit berbeda adalah memposting pemberitahuan khusus (ini, dengan nama khusus selain UITextFieldTextDidChangeNotification) jika Anda benar-benar perlu tahu apakah pemberitahuan itu milik Anda atau "buatan iOS".

EDIT:

Saya baru saja menemukan pendekatan berbeda yang menurut saya bisa lebih baik:

Ini melibatkan fitur Key-Value Observing (KVO) dari Objective-C ( http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/KeyValueObserving/KeyValueObserving.html#//apple_ref/doc/uid / 10000177-BCICJDHA ).

Pada dasarnya, Anda mendaftarkan diri sebagai pengamat properti dan jika properti ini berubah, Anda akan diberi tahu tentangnya. "Prinsip" sangat mirip dengan cara NSNotificationCenterkerjanya, menjadi keuntungan utama bahwa pendekatan ini bekerja secara otomatis juga pada iOS 6 (tanpa tweak khusus seperti harus memposting pemberitahuan secara manual).

Untuk UITextFieldskenario kami, ini berfungsi dengan baik jika Anda menambahkan kode ini, misalnya, Anda UIViewControlleryang berisi bidang teks:

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];

}

Kredit untuk jawaban ini mengenai manajemen "konteks": https://stackoverflow.com/a/12097161/2078512

Catatan: Sepertinya saat Anda sedang dalam proses mengedit a UITextFielddengan keyboard iOS bawaan, properti "teks" dari bidang teks tidak diperbarui dengan setiap huruf baru diketik / dihapus. Sebaliknya, objek bidang teks akan diperbarui "secara keseluruhan" setelah Anda mengundurkan diri status responden pertama dari bidang teks.

ercolemtar
sumber
5
Mengapa melibatkan diri sendiri, gunakan saja metode target-aksi dari jawaban XenElement. Jika Anda membutuhkan puluhan baris kode, lakukan sesuatu yang "seharusnya" sederhana, Anda mungkin salah melakukannya.
jrturton
6
Itu tampaknya perilaku yang dimaksudkan. Tidak ada metode delegasi lain (mis. Pengguliran, seleksi) dipanggil untuk acara yang berasal dari kode.
jrturton
1
Perhatikan bahwa UIKit tidak dijamin sesuai dengan KVO , sehingga solusi KVO tidak akan selalu berfungsi.
Pang
@jrturton, mengenai pertanyaan "mengapa" Anda, benar-benar lumrah bahwa di beberapa kelas lain (mungkin hiasan, animasi, atau sejenisnya) Anda perlu menanggapi apa yang terjadi di bidang teks.
Fattie
27

Kami dapat dengan mudah mengonfigurasikannya dari Storyboard, CTRL seret @IBActiondan ubah acara sebagai berikut:

masukkan deskripsi gambar di sini

bikram sapkota
sumber
14

Di sini dalam versi cepat untuk hal yang sama.

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

func textFieldDidChange(textField: UITextField) {

}

Terima kasih

Hindu
sumber
12

Saya menyelesaikan masalah yang mengubah perilaku shouldChangeChractersInRange. Jika Anda mengembalikan TIDAK, perubahan tidak akan diterapkan oleh iOS secara internal, sebagai gantinya Anda memiliki kesempatan untuk mengubahnya secara manual dan melakukan tindakan apa pun setelah perubahan.

- (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
}
Pauls
sumber
3
Masalah dengan solusi ini adalah: Posisi kursor diatur ke AKHIR bidang (segera setelah Anda melakukan textField.text = ...). Jadi, jika pengguna ingin mengedit karakter di tengah bidang, maka dengan setiap penekanan tombol, kursor mereka akan pergi ke ujung bidang. Cobalah dan lihatlah.
FranticRock
Poin bagus @Alex, tapi sederhana untuk dikerjakan. Hitung saja nilai yang dihasilkan di NSString lokal dan berikan itu ke metode didChangeTextInTextField: toText: Anda, lalu kembalikan YES untuk membiarkan iOS melakukan pembaruan.
jk7
11

Versi Swift diuji:

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

Parameter menjelaskan:

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

Kemudian tambahkan metode yang Anda buat di atas di UIViewController:

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

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

}
hantu kemicofa
sumber
7

Cepat 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
sumber
5

Untuk Swift 3.0:

let textField = UITextField()

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

menggunakan kelas seperti:

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

    }
}
pedrouan
sumber
4

Versi Swift 3

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

Dan dapatkan perubahannya di sini

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

Semoga ini bisa membantu.

Riajur Rahman
sumber
2

Anda harus menggunakan notifikasi untuk menyelesaikan masalah ini, karena metode lain akan mendengarkan kotak input bukan input sebenarnya, terutama ketika Anda menggunakan metode input Cina. Di viewDidload

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

kemudian

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

}

Akhirnya, Anda lari, akan dilakukan.

HsuChihYung
sumber
2

Swift 3.1:

Selector: ClassName.MethodName

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

  func fieldChanged(textfieldChange: UITextField){

    }
Beyaz
sumber
2

Versi 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

   }

}

Jangan lupa mengatur delegasi.

Joseph Francis
sumber
Saya memiliki 6 bidang teks pada pengontrol tampilan saya dan saya hanya ingin memeriksa apakah bidang teks terakhir telah diubah cetak ("Bidang Teks terakhir telah Diubah!") Dapatkah Anda membantu?
Saeed Rahmatolahi
Pertama buat Iboutlet untuk textField Anda. Kemudian atur delegasi dan tambahkan Target seperti yang saya tunjukkan dalam jawaban saya. Dalam fungsi delegasi textFieldDidChange, tambahkan ini: "jika textField == yourLastTextField {print (" Field Teks terakhir telah Diubah! ")} Saat ini saya tidak memiliki akses ke komputer saya. Saya akan membuat komentar ini lebih mudah dibaca ketika saya ke komputer saya
Joseph Francis
2

Dengan penutupan:

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

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

dan menggunakan

textField.targetAction? = {
 // will fire on text changed
 }
ebohdas
sumber
2
  1. Solusi KVO tidak berfungsi

KVO TIDAK bekerja di iOS untuk kontrol: http://stackoverflow.com/a/6352525/1402846 https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/KVO.html

  1. Di kelas mengamati, Anda melakukan ini:

Karena Anda tahu tampilan teks yang ingin Anda tonton:

var watchedTextView: UITextView!

Melakukan hal ini:

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

Namun, berhati-hatilah dengan itu:

  • kemungkinan Anda hanya ingin menelepon sekali , jadi jangan panggil, misalnya,layoutSubviews

  • cukup sulit untuk mengetahui kapan sebaiknya menyebutnya selama proses pemunculan Anda. Itu akan tergantung pada situasi Anda. Sayangnya tidak ada solusi standar dan terkunci

  • misalnya Anda biasanya pasti tidak bisa meneleponnya inittepat waktu, karena tentu saja watchedTextViewmungkin belum ada

.

  1. masalah selanjutnya adalah ...

Tidak ada pemberitahuan yang dipanggil saat teks diubah secara programatis .

Ini adalah gangguan besar, kuno, dan bodoh dalam rekayasa iOS.

Kontrol sama sekali tidak - akhir cerita - panggilan pemberitahuan ketika properti .text diubah secara pemrograman.

Ini sangat menyebalkan karena tentu saja - jelas - setiap aplikasi yang pernah dibuat menetapkan teks secara terprogram, seperti membersihkan bidang setelah posting pengguna, dll.

Anda harus subklas tampilan teks (atau kontrol serupa) seperti ini:

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

    }

}

(Kiat - jangan lupa panggilan super harus datang sebelumnya ! Panggilan pos.)

Tidak ada solusi yang tersedia, kecuali, Anda memperbaiki kontrol dengan subclass seperti yang ditunjukkan di atas. Itulah satu - satunya solusi.

Perhatikan notifikasi itu

UITextView.textDidChangeNotification

hasil dalam

func textViewDidChangeSelection(_ textView: UITextView) 

dipanggil.

( Tidak textViewDidChange .)

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



- (void) didChangeTextViewText {
 //do something
}
PeiweiChen
sumber
0

Satu hal adalah Anda mungkin memiliki beberapa UITextFields. Jadi, beri mereka tag dan kemudian Anda dapat mengaktifkan tag. Berikut cara mengatur pengamat di kelas apa saja.

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
sumber
-1

Versi Swift 4

Menggunakan Key-Value Observing Beri tahu objek tentang perubahan pada properti objek lain.

var textFieldObserver: NSKeyValueObservation?

textFieldObserver = yourTextField.observe(\.text, options: [.new, .old]) { [weak self] (object, changeValue) in
  guard let strongSelf = self else { return }
  print(changeValue)
}
Valeriy
sumber
Ide bagus. dapatkah Anda membungkusnya dalam konteks dan memberi tahu kami?
Revanth Kausikan
1
Sayangnya, ini benar-benar salah. KVO TIDAK bekerja di iOS untuk kontrol: stackoverflow.com/a/6352525/1402846 developer.apple.com/library/archive/documentation/General/…
Fattie