【问题标题】:UITextField text change eventUITextField 文本更改事件
【发布时间】:2011-10-24 01:03:18
【问题描述】:

如何检测文本字段中的任何文本更改?委托方法shouldChangeCharactersInRange 适用于某些东西,但它并不能完全满足我的需要。因为在它返回 YES 之前,其他观察者方法无法使用 textField 文本。

例如在我的代码calculateAndUpdateTextFields 中没有得到更新的文本,用户输入了。

他们有什么方法可以得到textChanged 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;
}

【问题讨论】:

标签: ios objective-c swift event-handling uitextfielddelegate


【解决方案1】:

来自proper way to do uitextfield text change call back

我捕捉到发送到 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 方法中,您可以检查文本字段的内容,并根据需要重新加载表格视图。

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

【讨论】:

  • 这是更好的解决方案 - 因为你也可以在 Nibs 或 Storyboards 中设置它,而且你不必写出过多的 UITextFieldTextDidChangeNotification 代码
  • 唯一的问题是。它不适用于我 - (BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
  • @iWheelBuy 只有当它返回NO 时,这是合乎逻辑的,因为当你从这个方法返回NO 时,你基本上是在说字段中的文本不应该改变。跨度>
  • 这对于编辑引起的更改非常有用。它不会捕获通过以下方式发生的程序更改:textField.text = "Some new value";。有没有聪明的方法来捕捉这个?
  • 你会认为 Apple 的 UITextFieldDelegate 会包含 func textField: UITextField, didChangeText text: String 之类的东西,但是...... (让 Apple 的人看起来很脏)跨度>
【解决方案2】:

XenElement 的回答很到位。

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

【讨论】:

  • 这可能很容易,但经过 10-15 分钟的搜索,直到偶然发现您的答案,我才发现这种可能性。再给我一个+1;很高兴基于代码和基于 IB 的解决方案都对此类问题进行了高度投票。
  • 我刚刚在 6.3 中检查了它,它就在那里。这是一个 UITextView 并右键单击它并查看“Editing Changed”。
  • 为什么它被命名为Editing Changed??疯狂的!不过感谢您的解决方案:-)
  • 因为在编辑更改时会调用该事件,而在文本字段完成编辑即辞职第一响应者时会发生 Value Changed。 UITextFieldTextDidChangeNotification 是一个 UITextField 通知,而 UIControlEventValueChanged 是由 UIButton 子类的 UIControl 实现的。
  • 我注意到这不起作用,因为用户从文本字段中跳出标签并且自动更正的文本替换了内容
【解决方案3】:

如此处所述:UITextField text change event,似乎从 iOS 6 开始(检查了 iOS 6.0 和 6.1)仅通过观察 @ 就无法完全检测到 UITextField 对象的变化987654327@。

现在似乎只跟踪那些直接由内置 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];

这样,当您更改 UITextField 对象的 .text 属性时,无论是在代码中“手动”更新它还是通过内置iOS 键盘。

考虑到这一点很重要,因为这不是记录在案的行为,这种方法可能会导致收到 2 个通知,因为您的 UITextField 对象中的相同更改。根据您的需求(当您的 UITextField.text 更改时您实际执行的操作),这可能会给您带来不便。

如果您确实需要知道通知是您的通知还是“iOS 制造的”通知,则稍有不同的方法是发布自定义通知(即使用UITextFieldTextDidChangeNotification 以外的自定义名称)。

编辑:

我刚刚找到了一种我认为可能会更好的不同方法:

这涉及到 Objective-C (http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/KeyValueObserving/KeyValueObserving.html#//apple_ref/doc/uid/10000177-BCICJDHA) 的 Key-Value Observing (KVO) 功能。

基本上,您将自己注册为属性的观察者,如果该属性发生更改,您会收到通知。 “原理”与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 时,文本字段的“文本”属性不会随着每个新字母而更新键入/删除。相反,在您退出文本字段的第一响应者状态后,文本字段对象会“整体”更新。

【讨论】:

  • 为什么要让自己经历这个,只需使用 XenElement 答案中的 target-action 方法。如果你需要几十行代码做一些“应该”简单的事情,你可能做错了。
  • 这似乎是预期的行为。代码引发的事件不会调用其他委托方法(例如滚动、选择)。
  • 请注意UIKit is not guaranteed to be KVO compliant,所以KVO解决方案不一定有效。
  • @jrturton ,关于您的“为什么”问题,在其他一些类(可能是装饰、动画等)中,您需要对文本字段中发生的事情做出响应是完全司空见惯的。
【解决方案4】:

设置事件监听器:

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

真正倾听:

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

【讨论】:

  • 酷。我喜欢这种方法,因为我想在已经是 TextFieldDelegate 的 ViewController 基类中实现 textField-change-listening。这样我就可以像魅力一样为(子视图中的文本字段)添加Target:baseControllerMethod。谢了!
【解决方案5】:

我解决了更改 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
}

【讨论】:

  • 此解决方案的问题是:光标位置设置为字段的末尾(只要您执行 textField.text = ...)。因此,如果用户想要编辑字段中间的字符,那么每次击键时,他们的光标都会移动到字段的末尾。试试看。
  • 好点 @Alex ,但很容易解决。只需在本地 NSString 中计算结果值并将其传递给您的 didChangeTextInTextField:toText: 方法,然后返回 YES 让 iOS 执行更新。
【解决方案6】:

这里有相同的快速版本。

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

func textFieldDidChange(textField: UITextField) {

}

谢谢

【讨论】:

    【解决方案7】:
    [[NSNotificationCenter defaultCenter] addObserver:self 
    selector:@selector(didChangeTextViewText:) 
    name:UITextFieldTextDidChangeNotification object:nil];
    
    
    
    - (void) didChangeTextViewText {
     //do something
    }
    

    【讨论】:

      【解决方案8】:

      斯威夫特:

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

      然后,实现回调函数:

      @objc final private func yourHandler(textField: UITextField) {
          print("Text changed")
      }
      

      【讨论】:

      • 为我工作,我已经尝试过了,但没有意识到该函数需要一个 textField 参数。如果这对您不起作用,请确保检查您的选择器是否具有要传入的 textField 的参数(即使您不使用它)。
      • 在 textFieldDidChange 中的 textField 之前添加 _,如下所示: func textFieldDidChange(textField: UITextField) { ... }
      【解决方案9】:

      你应该使用通知来解决这个问题,因为其他方法会听输入框而不是实际输入,特别是当你使用中文输入法时。 在 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;
      } 
      

      }

      最后,你跑,就完成了。

      【讨论】:

        【解决方案10】:

        已测试的 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!)
        
        }
        

        【讨论】:

          【解决方案11】:

          我们可以从Storyboard轻松配置,CTRL拖动@IBAction并更改事件如下:

          【讨论】:

            【解决方案12】:

            对于 Swift 3.0:

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

            使用类如:

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

            【讨论】:

              【解决方案13】:

              斯威夫特 3.1:

              选择器:类名.方法名

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

              【讨论】:

                【解决方案14】:

                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("last Text Field has been Changed!") 你能帮忙吗?
                • 首先为您的文本字段制作 Iboutlet。然后像我在回答中显示的那样设置委托和 addTarget。在委托函数 textFieldDidChange 中,添加:“if textField == yourLastTextField { print("last Text Field has been Changed!") } 现在我无法访问我的计算机。当我到我的电脑上。
                【解决方案15】:

                关闭:

                   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
                 }
                

                【讨论】:

                  【解决方案16】:

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

                  【讨论】:

                    【解决方案17】:

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

                    希望对你有帮助。

                    【讨论】:

                      【解决方案18】:

                      Swift 4 版本

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

                      【讨论】:

                      【解决方案19】:
                      1. KVO 解决方案不起作用

                      KVO 不适用于 iOS 控件:http://stackoverflow.com/a/6352525/1402846https://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。)

                      【讨论】:

                        【解决方案20】:

                        一件事是你可能有多个 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)
                        }
                        

                        【讨论】:

                          【解决方案21】:

                          SwiftUI

                          如果您使用原生 SwiftUI TextField 或仅使用 UIKit UITextField (here is how),您可以观察以下文本变化:

                          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),但它会返回更改而不是整个值。

                          【讨论】:

                            【解决方案22】:

                            从 iOS 14 开始不需要做选择器,你可以传递一个带有闭包的UIAction

                            let editingChanged = UIAction { _ in
                                // Do something when text changes...
                            }
                            myTextField.addAction(editingChanged, for: .editingChanged)
                            

                            【讨论】:

                              猜你喜欢
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 2011-06-09
                              • 2012-12-04
                              • 2014-01-26
                              • 2011-10-09
                              • 2011-08-28
                              相关资源
                              最近更新 更多