Sự kiện thay đổi văn bản UITextField


563

Làm cách nào tôi có thể phát hiện bất kỳ thay đổi văn bản nào trong textField? Phương thức đại biểu shouldChangeCharactersInRangehoạt động cho một cái gì đó, nhưng nó không đáp ứng chính xác nhu cầu của tôi. Vì cho đến khi nó trả về CÓ, các văn bản textField không có sẵn cho các phương thức quan sát khác.

ví dụ trong mã của tôi calculateAndUpdateTextFieldskhông nhận được văn bản cập nhật, người dùng đã gõ.

Có cách nào của họ để có được một cái gì đó như textChangedtrình xử lý sự kiện 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
Đây là một ví dụ tốt về câu trả lời SO với 1000 phiếu hoàn toàn không chính xác . iOS rất phức tạp, tinh tế và đầy rẫy.
Fattie

Câu trả lời:


1056

Từ cách thích hợp để thực hiện thay đổi văn bản uitextfield gọi lại :

Tôi bắt các ký tự được gửi tới điều khiển UITextField giống như thế này:

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

Trong Mục tiêu-C:

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

Trong Swift:

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

Sau đó, trong textFieldDidChangephương thức bạn có thể kiểm tra nội dung của textField và tải lại chế độ xem bảng của bạn khi cần.

Bạn có thể sử dụng nó và đặt tính toánAndUpdateTextFields làm của bạn selector.


18
Đây là giải pháp tốt hơn - bởi vì bạn cũng có thể đặt điều này trong Nibs hoặc Storyboards và bạn không phải viết ra mã UITextFieldTextDidChangeNotification quá mức
PostCodeism

3
Vấn đề duy nhất là. Nó không hoạt động với tôi cùng với - (BOOL) textField: (UITextField *) textField nênChangeChar characterInRange: (NSRange) thay thế phạm viString: (NSString *) chuỗi
iWheelBuy

4
@iWheelBuy Chỉ khi nó trả về NO, điều này hợp lý, bởi vì khi bạn trở về NOtừ phương thức này, về cơ bản, bạn đang nói rằng văn bản trong trường không nên thay đổi.
gitaarik

2
Điều này là tuyệt vời để chỉnh sửa gây ra thay đổi. Nó không nắm bắt được những thay đổi theo chương trình xảy ra thông qua : textField.text = "Some new value";. Có một cách thông minh để nắm bắt điều này?
Stewohn 16/1/2015

10
Bạn có thể nghĩ với Apple UITextFieldDelegaterằng một cái gì đó giống như func textField: UITextField, didChangeText text: Stringsẽ được đưa vào, nhưng ... (mang lại cho những người ở Apple một cái nhìn bẩn thỉu)
Brandon A

394

Câu trả lời của XenEuity là tại chỗ.

Những điều trên có thể được thực hiện trong trình xây dựng giao diện bằng cách nhấp chuột phải vào UITextField và kéo sự kiện gửi "Chỉnh sửa thay đổi" đến đơn vị lớp con của bạn.

Sự kiện thay đổi UITextField


2
Có thể dễ dàng, nhưng sau 10 - 15 phút tìm kiếm, tôi đã không phát hiện ra khả năng này cho đến khi tình cờ tìm thấy câu trả lời của bạn. Có thêm +1 từ tôi; thật tuyệt khi có cả các giải pháp dựa trên mã và IB được bình chọn cao cho các câu hỏi như thế này.
Đánh dấu Amery

Tôi chỉ kiểm tra nó trong 6.3 và nó ở đó. Đây là một UITextView và nhấp chuột phải vào nó và xem "Chỉnh sửa thay đổi".
William T.

4
Tại sao trên trái đất nó có tên là Chỉnh sửa thay đổi ?? điên! cảm ơn về giải pháp mặc dù :-)
malhal

3
Bởi vì sự kiện được gọi khi chỉnh sửa thay đổi, trong khi Giá trị thay đổi xảy ra khi trường văn bản kết thúc chỉnh sửa tức là từ bỏ phản hồi đầu tiên. UITextFieldTextDidChangeNotification là một thông báo UITextField, trong khi UIControlEventValueChanged được thực hiện bởi UIControl mà lớp con UIButton.
Camsoft

2
Tôi nhận thấy điều này không hoạt động khi các tab người dùng ra khỏi trường văn bản và một văn bản tự động sửa lỗi thay thế nội dung
noobular

136

để đặt trình nghe sự kiện:

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

để thực sự lắng nghe:

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

mát mẻ. Tôi thích cách tiếp cận này, vì tôi muốn triển khai lắng nghe thay đổi văn bản trong lớp cơ sở ViewContoder đã là TextFieldDelegate. Bằng cách này, tôi có thể addTarget: baseControllMethod cho (textField trong các cuộc phỏng vấn) như một cơ duyên. CÁM ƠN!
HBublitz

87

Nhanh:

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

Sau đó, thực hiện chức năng gọi lại:

@objc func textFieldDidChange(textField: UITextField){

print("Text changed")

}

2
Làm việc cho tôi, tôi đã thử điều này nhưng không nhận ra hàm cần một đối số textField. Nếu điều này không hiệu quả với bạn, hãy đảm bảo kiểm tra xem bộ chọn của bạn có đối số cho textField được truyền vào không (ngay cả khi bạn không sử dụng nó).
werm098

Thêm _ trước textField trong textFieldDidChange như thế này: func textFieldDidChange (textField: UITextField) {...}
Makalele

30

Như đã nêu ở đây: Sự kiện thay đổi văn bản UITextField , dường như kể từ iOS 6 (đã kiểm tra iOS 6.0 và 6.1), không thể phát hiện đầy đủ các thay đổi trong UITextFieldcác đối tượng chỉ bằng cách quan sát UITextFieldTextDidChangeNotification.

Có vẻ như chỉ những thay đổi được thực hiện trực tiếp bởi bàn phím iOS tích hợp mới được theo dõi ngay bây giờ. Điều này có nghĩa là nếu bạn thay đổi UITextFieldđối tượng của mình chỉ bằng cách gọi một cái gì đó như thế này : myUITextField.text = @"any_text", bạn sẽ không được thông báo về bất kỳ thay đổi nào cả.

Tôi không biết đây là một lỗi hay nó được dự định. Có vẻ như một lỗi vì tôi không tìm thấy bất kỳ lời giải thích hợp lý trong tài liệu. Điều này cũng được nêu ở đây: sự kiện thay đổi văn bản UITextField .

"Giải pháp" của tôi cho vấn đề này là thực sự tự mình gửi một thông báo cho mọi thay đổi tôi thực hiện UITextField(nếu thay đổi đó được thực hiện mà không cần sử dụng bàn phím iOS tích hợp). Một cái gì đó như thế này:

myUITextField.text = @"I'm_updating_my_UITextField_directly_in_code";

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

[NSNotificationCenter.defaultCenter 
  postNotification:myTextFieldUpdateNotification];

Bằng cách này, bạn tự tin 100% rằng bạn sẽ nhận được thông báo tương tự khi bạn thay đổi thuộc .texttính của UITextFieldđối tượng, khi bạn cập nhật "thủ công" trong mã của mình hoặc thông qua bàn phím iOS tích hợp.

Điều quan trọng là phải xem xét rằng, vì đây không phải là hành vi được ghi lại, cách tiếp cận này có thể dẫn đến 2 thông báo nhận được cho cùng một thay đổi trong UITextFieldđối tượng của bạn . Tùy thuộc vào nhu cầu của bạn (những gì bạn thực sự làm khi UITextField.textthay đổi) đây có thể là một sự bất tiện cho bạn.

Một cách tiếp cận hơi khác sẽ là đăng một thông báo tùy chỉnh (đây là, với một tên tùy chỉnh khác UITextFieldTextDidChangeNotification) nếu bạn thực sự cần biết liệu thông báo đó là của bạn hay "do iOS tạo ra".

BIÊN TẬP:

Tôi vừa tìm thấy một cách tiếp cận khác mà tôi nghĩ có thể tốt hơn:

Điều này liên quan đến tính năng Quan sát giá trị khóa (KVO) của Objective-C ( http://developer.apple.com/l Library / ios / # document.com / cocoa / conception / 10000177-BCICJDHA ).

Về cơ bản, bạn đăng ký cho mình như một người quan sát một tài sản và nếu tài sản này thay đổi, bạn sẽ được thông báo về nó. "Nguyên tắc" khá giống với cách NSNotificationCenterhoạt động, là ưu điểm chính mà phương pháp này hoạt động tự động kể từ iOS 6 (không có bất kỳ tinh chỉnh đặc biệt nào như phải đăng thông báo thủ công).

Đối với UITextField-scenario của chúng tôi, nó hoạt động tốt nếu bạn thêm mã này vào, ví dụ, mã của bạn UIViewControllercó chứa trường văn bản:

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

}

Tín dụng cho câu trả lời này liên quan đến quản lý "bối cảnh": https://stackoverflow.com/a/12097161/2078512

Lưu ý: Có vẻ như trong khi bạn đang trong quá trình chỉnh sửa UITextFieldbằng bàn phím iOS tích hợp, thuộc tính "văn bản" của trường văn bản không được cập nhật với mỗi chữ cái mới được gõ / xóa. Thay vào đó, đối tượng trường văn bản được cập nhật "toàn bộ" sau khi bạn từ bỏ trạng thái phản hồi đầu tiên của trường văn bản.


5
Tại sao phải đặt mình vào vấn đề này, chỉ cần sử dụng phương pháp hành động mục tiêu từ câu trả lời của XenEuity. Nếu bạn cần hàng chục dòng mã, hãy làm điều gì đó "đơn giản", có lẽ bạn đã làm sai.
jrturton

6
Đó dường như là hành vi dự định. Không có phương thức ủy nhiệm nào khác (ví dụ: cuộn, chọn) được gọi cho các sự kiện có nguồn gốc từ mã.
jrturton

1
Lưu ý rằng UIKit không được đảm bảo tuân thủ KVO , vì vậy giải pháp KVO sẽ không nhất thiết phải hoạt động.
Pang

@jrturton, liên quan đến câu hỏi "tại sao" của bạn, nó hoàn toàn phổ biến rằng trong một số lớp khác (có thể là trang trí, hoạt hình hoặc tương tự), bạn cần trả lời những gì đang xảy ra trong trường văn bản.
Fattie

27

Chúng tôi có thể dễ dàng định cấu hình từ đó Storyboard, CTRL kéo @IBActionvà thay đổi sự kiện như sau:

nhập mô tả hình ảnh ở đây


14

Ở đây trong phiên bản nhanh chóng cho cùng.

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

func textFieldDidChange(textField: UITextField) {

}

Cảm ơn


12

Tôi đã giải quyết vấn đề thay đổi hành vi của ShouldChangeChractersInRange. Nếu bạn trả về KHÔNG, các thay đổi sẽ không được iOS áp dụng trong nội bộ, thay vào đó bạn có cơ hội thay đổi thủ công và thực hiện bất kỳ hành động nào sau khi thay đổi.

- (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
Vấn đề với giải pháp này là: Vị trí Con trỏ được đặt thành KẾT THÚC của trường (ngay khi bạn thực hiện textField.text = ...). Vì vậy, nếu người dùng muốn chỉnh sửa các ký tự ở giữa trường, thì với mỗi lần nhấn phím, con trỏ của họ sẽ đi đến cuối trường. Hãy thử nó và xem.
FranticRock

Điểm tốt @Alex, nhưng đơn giản để làm việc xung quanh. Chỉ cần tính giá trị kết quả trong một NSString cục bộ và chuyển giá trị đó cho phương thức didChangeTextInTextField: toText: của bạn, sau đó trả về CÓ để cho phép iOS thực hiện cập nhật.
jk7

11

Phiên bản Swift đã thử nghiệm:

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

Các thông số giải thích:

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

Sau đó thêm phương thức bạn đã tạo ở trên vào UIViewController:

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

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

}

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

}

5

Đối với Swift 3.0:

let textField = UITextField()

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

sử dụng lớp như:

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

    }
}

4

Phiên bản Swift 3

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

Và nhận được những thay đổi ở đây

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

Hy vọng nó giúp.


2

Bạn nên sử dụng thông báo để giải quyết vấn đề này, bởi vì phương thức khác sẽ lắng nghe hộp đầu vào chứ không phải đầu vào thực sự, đặc biệt là khi bạn sử dụng phương thức nhập liệu của Trung Quốc. Trong viewDidload

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

sau đó

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

}

cuối cùng, bạn chạy, sẽ xong.


2

Swift 3,1:

Bộ chọn: ClassName.MethodName

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

  func fieldChanged(textfieldChange: UITextField){

    }

2

Phiên bản 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

   }

}

Đừng quên thiết lập đại biểu.


Tôi có 6 trường văn bản trong trình điều khiển chế độ xem của mình và tôi chỉ muốn kiểm tra xem trường văn bản cuối cùng đã được thay đổi in chưa ("Trường văn bản cuối cùng đã được thay đổi!") Bạn có thể giúp gì không?
Saeed Rahmatolahi

Đầu tiên tạo Iboutlet cho textField của bạn. Sau đó, đặt đại biểu và addTarget như tôi đã trình bày trong câu trả lời của mình. Trong hàm ủy nhiệm textFieldDidChange, hãy thêm cái này: "if textField == yourLastTextField {print (" Trường văn bản cuối cùng đã được thay đổi! ")} Ngay bây giờ tôi không có quyền truy cập vào máy tính của mình. vào máy tính của tôi.
Joseph Francis

2

Với việc đóng cửa:

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

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

và sử dụng

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

2
  1. Giải pháp KVO không hoạt động

KVO KHÔNG hoạt động trong iOS đối với các điều khiển: http://stackoverflow.com/a/6352525/1402846 https://developer.apple.com/l Library / archive / document / General / Contualual / DevPedia-CocoaCore / KBO.html

  1. Trong lớp quan sát, bạn làm điều này:

Cho rằng bạn biết chế độ xem văn bản bạn muốn xem:

var watchedTextView: UITextView!

Làm cái này:

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

Tuy nhiên, hãy cẩn thận với điều đó:

  • có vẻ như bạn chỉ muốn gọi nó một lần , vì vậy đừng gọi nó vào, ví dụ,layoutSubviews

  • thật khó để biết khi nào nên gọi nó là tốt nhất trong quá trình mang lên của bạn. Nó sẽ phụ thuộc vào tình hình của bạn. Thật không may, không có giải pháp khóa, tiêu chuẩn

  • ví dụ bạn thường chắc chắn không thể gọi nó vào initthời điểm đó, vì tất nhiên watchedTextViewcó thể chưa tồn tại

.

  1. vấn đề tiếp theo là ...

Không có thông báo nào được gọi khi văn bản được thay đổi theo chương trình .

Đây là một sự phiền toái lớn, lâu đời và ngu ngốc trong kỹ thuật iOS.

Điều khiển đơn giản là không - kết thúc câu chuyện - gọi các thông báo khi thuộc tính .text được thay đổi theo chương trình.

Điều này cực kỳ khó chịu vì tất nhiên - rõ ràng - mọi ứng dụng từng được thực hiện sẽ đặt văn bản theo chương trình, chẳng hạn như xóa trường sau khi người dùng đăng, v.v.

Bạn phải phân lớp chế độ xem văn bản (hoặc điều khiển tương tự) như thế này:

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

    }

}

(Mẹo - đừng quên siêu cuộc gọi phải đến trước ! Cuộc gọi bài.)

Không giải pháp khả dụng, trừ khi, bạn sửa điều khiển bằng cách phân lớp như hiển thị ở trên. Đó là giải pháp duy nhất .

Lưu ý rằng thông báo

UITextView.textDidChangeNotification

kết quả trong

func textViewDidChangeSelection(_ textView: UITextView) 

được gọi là.

( Không phải textViewDidChange .)


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



- (void) didChangeTextViewText {
 //do something
}

0

Một điều là bạn có thể có nhiều UITextField. Vì vậy, cung cấp cho họ một thẻ và sau đó bạn có thể bật các thẻ. Đây là cách thiết lập một người quan sát trong bất kỳ lớp nào khá nhiều.

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

Phiên bản Swift 4

Sử dụng Quan sát giá trị khóa Thông báo cho các đối tượng về các thay đổi đối với các thuộc tính của các đối tượng khác.

var textFieldObserver: NSKeyValueObservation?

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

Ý tưởng tốt. bạn có thể gói nó trong một bối cảnh và cho chúng tôi biết?
Revanth Kausikan

1
Thật không may, điều này là hoàn toàn sai. KVO KHÔNG hoạt động trong iOS để kiểm soát: stackoverflow.com/a/6352525/1402846 developer.apple.com/l
Library / archive / document / General / /
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.