611

如何检测文本字段中的任何文本更改?委托方法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;
}
4

22 回答 22

1131

正确的方式进行 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 的内容,并根据需要重新加载表视图。

您可以使用它并将calculateAndUpdateTextFields作为您的selector.

于 2011-08-10T12:21:10.383 回答
411

XenElement 的回答很到位。

通过右键单击 UITextField 并将“Editing Changed”发送事件拖到您的子类单元中,上述操作也可以在界面生成器中完成。

UITextField 更改事件

于 2012-12-05T17:36:14.933 回答
140

设置事件监听器:

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

实际聆听:

- (void)textFieldDidChange:(UITextField *)textField {
    NSLog(@"text changed: %@", textField.text);
}
于 2013-06-06T08:25:17.903 回答
107

迅速:

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

然后,实现回调函数:

@objc final private func yourHandler(textField: UITextField) {
    print("Text changed")
}
于 2016-03-14T22:00:28.163 回答
32

如此处所述:UITextField text change event,似乎从 iOS 6(iOS 6.0 和 6.1 检查)开始,UITextField仅通过观察UITextFieldTextDidChangeNotification.

现在似乎只跟踪那些由内置 iOS 键盘直接进行的更改。UITextField这意味着,如果您仅通过调用以下内容来更改对象: myUITextField.text = @"any_text",您将不会收到任何更改的通知。

我不知道这是一个错误还是有意为之。似乎是一个错误,因为我在文档中没有找到任何合理的解释。这也在这里说明:UITextField text change event

我对此的“解决方案”实际上是为我对我所做的每一次更改发布一个通知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属性时,您将 100% 确信您将收到相同的通知。UITextField

重要的是要考虑到,由于这不是记录在案的行为,因此这种方法可能会导致收到 2 条通知,因为您的UITextField对象发生了相同的更改。根据您的需求(更改时您实际执行的操作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 键盘编辑 a的过程中,文本字段的“文本”属性不会随着输入/删除的每个新字母而更新。UITextField相反,在您退出文本字段的第一响应者状态后,文本字段对象会“整体”更新。

于 2013-02-19T17:07:59.240 回答
29

我们可以很容易地从Storyboard、CTRL 拖动@IBAction和更改事件进行配置,如下所示:

在此处输入图像描述

于 2016-08-22T09:30:17.460 回答
15

这里是相同的快速版本。

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

func textFieldDidChange(textField: UITextField) {

}

谢谢

于 2015-10-01T07:02:12.873 回答
13

我解决了改变 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
}
于 2013-11-11T15:30:03.207 回答
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!)

}
于 2016-04-25T08:45:10.433 回答
8

斯威夫特 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!)

}
于 2017-11-08T01:24:31.927 回答
6

从 iOS 14 开始,不需要制作选择器,您可以通过 aUIAction来代替:

let editingChanged = UIAction { _ in
    // Do something when text changes...
}
myTextField.addAction(editingChanged, for: .editingChanged)
于 2021-01-22T04:04:55.503 回答
5

对于 Swift 3.0:

let textField = UITextField()

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

使用类如:

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

    }
}
于 2016-09-04T16:31:17.540 回答
4

斯威夫特 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.
}

希望能帮助到你。

于 2017-12-04T06:58:12.550 回答
4
  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。)

于 2020-01-20T18:33:22.183 回答
3

斯威夫特 3.1:

选择器:ClassName.MethodName

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

  func fieldChanged(textfieldChange: UITextField){

    }
于 2017-04-11T11:32:34.940 回答
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;
} 

}

最后,你跑,就完成了。

于 2016-04-25T06:42:25.840 回答
2

斯威夫特 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

   }

}

不要忘记设置委托。

于 2017-06-06T02:36:35.110 回答
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
 }
于 2017-06-21T14:46:15.133 回答
1
[[NSNotificationCenter defaultCenter] addObserver:self 
selector:@selector(didChangeTextViewText:) 
name:UITextFieldTextDidChangeNotification object:nil];



- (void) didChangeTextViewText {
 //do something
}
于 2016-01-04T06:57:28.917 回答
1

一件事是你可能有多个 UITextFields。所以,给他们一个标签,然后你可以打开标签。以下是在任何类中设置观察者的方法。

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)
}
于 2020-05-02T16:51:14.823 回答
0

SwiftUI

如果您使用本机 SwiftUITextField或仅使用 UIKit UITextField这里是如何),您可以观察文本更改,例如:

SwiftUI 2.0

从 iOS 14、macOS 11 或任何其他包含 SwiftUI 2.0 的操作系统开始,有一个名为的新修饰符.onChange可以检测给定的任何更改state

struct ContentView: View {
    @State var text: String = ""

    var body: some View {
        TextField("Enter text here", text: $text)
            .onChange(of: text) {
                print($0) // You can do anything due to the change here.
                // self.autocomplete($0) // like this
            }
    }
}

SwiftUI 1.0

对于较旧的 iOS 和其他 SwiftUI 1.0 平台,您可以在combine框架onReceive的帮助下使用:

import Combine
.onReceive(Just(text)) { 
    print($0)
}

请注意,您可以使用text.publisher代替,Just(text)但它返回更改而不是整个值。

于 2020-11-04T07:49:50.660 回答
-1

斯威夫特 4 版本

使用 Key-Value Observing 通知对象有关其他对象属性的更改。

var textFieldObserver: NSKeyValueObservation?

textFieldObserver = yourTextField.observe(\.text, options: [.new, .old]) { [weak self] (object, changeValue) in
  guard let strongSelf = self else { return }
  print(changeValue)
}
于 2019-03-19T15:11:40.290 回答