UITextField metin değiştirme olayı


563

Bir textField öğesinde herhangi bir metin değişikliğini nasıl algılayabilirim? Delege yöntemishouldChangeCharactersInRange şey için çalışır, ancak ihtiyacımı tam olarak karşılamadı. EVET döndürene kadar textField metinleri diğer gözlemci yöntemleri tarafından kullanılamaz.

örneğin benim kod calculateAndUpdateTextFieldsgüncellenmiş metin alamadım, kullanıcı yazdı.

textChangedJava olay işleyicisi gibi bir şey elde etmek için herhangi bir yoludur .

- (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
Bu, tamamen yanlış olan 1000 oy içeren bir SO cevabının iyi bir örneğidir . iOS zor, suibtle ve gotchyalarla doludur.
Fattie

Yanıtlar:


1056

Gönderen düzgün bir şekilde metin değişikliği çağrısı geri UITextField yapmak :

Bir UITextField denetimine böyle bir şey gönderilen karakterleri yakalamak:

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

Hedef-C'de:

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

Swift'te:

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

Daha sonra textFieldDidChangeyöntemde textField içeriğini inceleyebilir ve tablo görünümünüzü gerektiği gibi yeniden yükleyebilirsiniz.

Bunu kullanmak ve koyabilirsiniz calculateAndUpdateTextFields sizin bizim gibi selector.


18
Bu daha iyi bir çözümdür - çünkü bunu Nibs veya Storyboard'larda da ayarlayabilirsiniz ve aşırı UITextFieldTextDidChangeNotification kodunu yazmanız gerekmez
PostCodeism

3
Tek sorun. Benim için işe yaramaz - (BOOL) textField: (UITextField *) textField shouldChangeCharactersInRange: (NSRange) aralık değiştirmeString: (NSString *) string
iWheelBuy

4
@iWheelBuy Yalnızca döndüğünde NO, bu mantıklıdır, çünkü NObu yöntemden döndüğünüzde , temel olarak alandaki metnin değişmemesi gerektiğini söylüyorsunuz.
gitaarik

2
Bu, neden olan değişiklikleri düzenlemek için mükemmeldir. İle gerçekleşen programlı değişiklikleri yakalamaz textField.text = "Some new value";. Bunu yakalamanın akıllıca bir yolu var mı?
Benjohn

10
Apple'larla UITextFieldDelegatebenzer bir şeyin func textField: UITextField, didChangeText text: Stringdahil olacağını düşünürdünüz, ama ... (Apple'daki adamlara kirli bir görünüm verir)
Brandon A

394

XenElement'in cevabı yerinde.

Yukarıda UITextField sağ tıklayarak ve "Düzenleme Değiştirildi" gönderme olayı alt sınıf birimine sürükleyerek arayüz oluşturucuda da yapılabilir.

UITextField Değişiklik Etkinliği


2
Kolay olabilir, ancak 10-15 dakikalık bir aramadan sonra, cevabınızı tesadüfen tökezleyene kadar bu olasılığı keşfetmemiştim. Benden bir +1 daha al; hem kod tabanlı hem de IB tabanlı çözümlerin böyle sorulara yüksek oy vermesi güzel.
Mark Amery

Ben sadece 6.3 yılında kontrol ve orada. Bu bir UITextView ve sağ tıklayın ve "Düzenleme Değiştirildi" bölümüne bakın.
William

4
neden dünyada Adlandı? deli! çözüm için teşekkürler :-)
malhal

3
Değişiklik düzenlenirken olay çağrıldığı için metin alanı düzenlemeyi bitirdiğinde Değişen Değer olur, yani ilk yanıtlayıcıyı istifa eder. UITextFieldTextDidChangeNotification bir UITextField bildirimi iken UIControlEventValueChanged, UIButton alt sınıflarının bulunduğu UIControl tarafından uygulanır.
Camsoft

2
Bunun metin alanından kullanıcı sekmeleri olarak işe yaramadığını ve içeriğin yerini alacak bir otomatik düzeltme metni olduğunu fark ediyorum
noobular

136

olay dinleyicisini ayarlamak için:

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

gerçekten dinlemek için:

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

güzel. Zaten TextFieldDelegate olan bir ViewController temel sınıfında textField-change-listen uygulamak istiyorum çünkü bu yaklaşımı seviyorum. Bu şekilde bir cazibe gibi addTarget: baseControllerMethod için (alt görünümlerde textField) ekleyebilirsiniz. TEŞEKKÜR!
HBublitz

87

Swift:

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

Ardından geri arama işlevini uygulayın:

@objc func textFieldDidChange(textField: UITextField){

print("Text changed")

}

2
Benim için çalıştım, bunu zaten denemiştim ama fonksiyonun bir textField argümanı gerektirdiğini fark etmedim. Bu sizin için işe yaramıyorsa, seçicinizde textField öğesinin iletilmesi için bir argüman olup olmadığını kontrol edin (kullanmasanız bile).
werm098

TextFieldDidChan içinde _ önce textField öğesini şu şekilde ekleyin: func textFieldDidChange (textField: UITextField) {...}
Makalele

30

Burada belirtildiği gibi: UITextField metin değişikliği olayı , iOS 6'dan (iOS 6.0 ve 6.1 kontrol edildi), UITextFieldnesnelerdeki değişiklikleri sadece gözlemleyerek tam olarak tespit etmek mümkün görünmüyor UITextFieldTextDidChangeNotification.

Görünüşe göre, yalnızca doğrudan yerleşik iOS klavyesi tarafından yapılan değişiklikler takip ediliyor. Bu, UITextFieldnesnenizi yalnızca şöyle bir şey çağırarak değiştirirseniz : myUITextField.text = @"any_text"hiçbir değişiklik hakkında bilgilendirilmeyeceğiniz anlamına gelir .

Bunun bir hata olup olmadığını veya amaçlandığını bilmiyorum. Belgelerde makul bir açıklama bulamadığım için bir hata gibi görünüyor. Bu burada da belirtilir: UITextField metin değiştirme olayı .

Benim "çözümüm" aslında yaptığım her değişiklik için kendim bir bildirim göndermektir UITextField(eğer bu değişiklik yerleşik iOS klavyesini kullanmadan yapılırsa). Bunun gibi bir şey:

myUITextField.text = @"I'm_updating_my_UITextField_directly_in_code";

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

[NSNotificationCenter.defaultCenter 
  postNotification:myTextFieldUpdateNotification];

Bu şekilde .text, UITextFieldnesnenizin özelliğini değiştirdiğinizde, kodunuzda "manuel olarak" güncellediğinizde veya yerleşik iOS klavyesi ile aynı bildirimi alacağınızdan% 100 emin olabilirsiniz .

Bu, belgelenmiş bir davranış olmadığından, bu yaklaşımın UITextFieldnesnenizdeki aynı değişiklik için alınan 2 bildirime yol açabileceğini düşünmek önemlidir . İhtiyaçlarınıza bağlı olarak ( UITextField.textdeğişikliklerinizde gerçekten ne yaptığınız) bu sizin için bir rahatsızlık olabilir.

Bildirimin UITextFieldTextDidChangeNotificationsize ait olup olmadığını veya "iOS yapımı" olup olmadığını gerçekten bilmeniz gerekiyorsa, biraz farklı bir yaklaşım özel bir bildirim yayınlamaktır (bu, farklı bir adla ).

DÜZENLE:

Daha iyi olabileceğini düşündüğüm farklı bir yaklaşım buldum:

Bu, Objective-C'nin Anahtar Değeri Gözlemleme (KVO) özelliğini içerir ( http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/KeyValueObserving/KeyValueObserving.html#//apple_ref/doc/uid / 10000177-BCICJDHA ).

Temel olarak, kendinizi bir mülkün gözlemcisi olarak kaydedersiniz ve bu özellik değişirse bu konuda bilgilendirilirsiniz. "Prensip" nasıl NSNotificationCenterçalıştığına oldukça benzer , bu yaklaşımın iOS 6'dan itibaren otomatik olarak da çalışmasının ana avantajıdır (bildirimleri manuel olarak göndermek zorunda kalmadan özel bir değişiklik yapmadan).

Bizim için UITextFieldsenin örneğin, bu kodu eklerseniz bu sadece çalışıyor -scenario, UIViewControllermetin alanı içerir:

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

}

"Bağlam" yönetimi ile ilgili bu yanıta verilen kredi: https://stackoverflow.com/a/12097161/2078512

Not: iken gibi görünüyor sürecinde bir düzenleme UITextFieldyerleşik iOS klavyesi, metin alanının "metin" özelliği her yeni harfle güncelleştirilmezse ile yazdığınız / kaldırıldı. Bunun yerine, metin alanının ilk yanıtlayıcı durumunu bıraktıktan sonra metin alanı nesnesi "bir bütün olarak" güncelleştirilir.


5
Kendinizi neden bu hale getirin, sadece XenElement'in cevabından hedef-aksiyon yöntemini kullanın. Onlarca kod satırına ihtiyacınız varsa, basit olması gereken bir şey yapın, muhtemelen yanlış yapıyorsunuzdur.
jrturton

6
Bu amaçlanan davranış gibi görünüyor. Kod kaynaklı olaylar için başka temsilci yöntemi (ör. Kaydırma, seçim) çağrılmaz.
jrturton

1
O Not UIKit KVO uyumlu olması garanti edilmez KVO çözüm olacak ille İşleriniz çok.
Pang

@jrturton, "neden" sorunuzla ilgili olarak, başka bir sınıfta (belki de bir dekorasyon, animasyon veya benzeri) metin alanında olup bitenlere yanıt vermeniz tamamen yaygın bir durumdur.
Fattie

27

Bunu StoryboardCTRL'yi sürükleyerek @IBActionve değiştir olayını aşağıdaki gibi kolayca yapılandırabiliriz :

resim açıklamasını buraya girin


14

Burada aynı hızlı sürümü.

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

func textFieldDidChange(textField: UITextField) {

}

Teşekkürler


12

ShouldChangeChractersInRange davranışını değiştirerek sorunu çözdüm. NO döndürürseniz, değişiklikler iOS tarafından dahili olarak uygulanmaz, bunun yerine manuel olarak değiştirme ve değişikliklerden sonra herhangi bir işlem yapma olanağınız vardır.

- (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
Bu çözümdeki sorun şudur: İmleç konumu, alanın SONUNDA ayarlanır (textField.text = ... yapar yapmaz). Dolayısıyla, kullanıcı alanın ortasındaki karakterleri düzenlemek istiyorsa, her tuş vuruşu ile imleci alanın sonuna gider. Deneyin ve görün.
FranticRock

@Alex iyi bir nokta, ama basit bir çözüm. Sonuçta elde edilen değeri yerel bir NSString içinde hesaplayın ve bunu didChangeTextInTextField: toText: yönteminize iletin, ardından iOS'un güncellemeyi yapmasına izin vermek için EVET'i döndürün.
jk7

11

Test edilen Swift Sürümü:

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

Açıklanan parametreler:

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

Ardından, yukarıda oluşturduğunuz yöntemi aşağıdakilere ekleyin UIViewController:

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

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

}

7

Hızlı 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 için:

let textField = UITextField()

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

sınıf kullanarak:

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

    }
}

4

Swift 3 Sürümü

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

Ve değişiklikleri buraya getirin

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

Umarım yardımcı olur.


2

Bu sorunu çözmek için bildirimi kullanmalısınız, çünkü diğer yöntem, özellikle Çince giriş yöntemini kullandığınızda, giriş kutusunu gerçek girişi değil dinleyecektir. ViewDidload'da

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

sonra

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

}

Sonunda, sen koş, yapacak.


2

Hızlı 3.1:

Seçici: ClassName.MethodName

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

  func fieldChanged(textfieldChange: UITextField){

    }

2

Swift 3 Sürümü:

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

   }

}

Delege kurmayı unutmayın.


Görünüm denetleyicimde 6 metin alanı var ve sadece son metin alanının değiştirilip değiştirilmediğini kontrol etmek istiyorum ("son Metin Alanı Değiştirildi!") Yardımcı olabilir misiniz?
Saeed Rahmatolahi

Önce textField için Iboutlet yapın. Sonra temsilci ve addTarget yanıtımda gösterdiğim gibi ayarlayın. Delegate textFieldDidChange işlevinde şunu ekleyin: "if textField == yourLastTextField {print (" son Metin Alanı Değiştirildi! ")} Şu anda bilgisayarıma erişimim yok. Bu yorumu daha okunabilir hale getireceğim Bilgisayarım olsun
Joseph Francis

2

Kapatma ile:

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

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

ve kullanarak

textField.targetAction? = {
 // will fire on text changed
 }

2
  1. KVO çözümleri çalışmıyor

KVO, iOS'ta kontroller için çalışmaz: http://stackoverflow.com/a/6352525/1402846 https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/KVO.html

  1. Gözlem sınıfında şunları yaparsınız:

İzlemek istediğiniz metin görünümünü bildiğiniz göz önüne alındığında:

var watchedTextView: UITextView!

Bunu yap:

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

Ancak buna dikkat edin:

  • muhtemelen sadece bir kez aramak istersin , bu yüzden aramayın, örneğin,layoutSubviews

  • getirme sürecinde ne zaman en iyi arayacağınızı bilmek oldukça zordur. Durumunuza bağlı olacaktır.Ne yazık ki standart, kilitli bir çözüm yok

  • örneğin genellikle kesinlikle olabilir değil de diyoruz inittabii beri, zamanwatchedTextView henüz mevcut olmayabilir

.

  1. sonraki sorun şu ki ...

Metin programlı olarak değiştirildiğinde hiçbir bildirim çağrılmaz .

Bu, iOS mühendisliğinde büyük, eski ve aptal bir sıkıntı.

Kontroller basitçe yapmaz - hikayenin sonu.Text özelliği programlı olarak değiştirildiğinde bildirimleri çağırmaz.

Tabii ki - tabii ki - yapılan her uygulama metni kullanıcı tarafından yayınlandıktan sonra alanı temizlemek gibi programlı olarak ayarlar.

Metin görünümünü (veya benzer kontrolü) aşağıdaki gibi alt sınıflara ayırmanız gerekir:

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

    }

}

(İpucu - süper çağrının daha önce gelmesi gerektiğini unutmayın ! Sonrası çağrısı.)

Orada hayır , sadece yukarıda gösterildiği gibi sınıflara göre kontrolünü düzeltmek sürece, çözüm mevcuttur. Yani tek çözüm.

Bildirimin

UITextView.textDidChangeNotification

sonuç

func textViewDidChangeSelection(_ textView: UITextView) 

aranmak.

( Yok textViewDidChange .)


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



- (void) didChangeTextViewText {
 //do something
}

0

Bir şey, birden fazla UITextFields olabilir. Bu nedenle, onlara bir etiket verin ve ardından etiketleri açabilirsiniz. Hemen hemen her sınıfta bir gözlemci nasıl kuracağınız aşağıda açıklanmıştır.

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 Sürümü

Anahtar Değeri Gözlemlemeyi Kullanma Nesneleri diğer nesnelerin özelliklerindeki değişiklikler hakkında bilgilendirin.

var textFieldObserver: NSKeyValueObservation?

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

İyi bir fikir. bunu bir bağlam içinde sarabilir ve bize bildirebilir misiniz?
Revanth Kausikan

1
Bu maalesef tamamen yanlış. KVO, iOS'ta kontroller için çalışmaz: stackoverflow.com/a/6352525/1402846 developer.apple.com/library/archive/documentation/General/…
Fattie
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.