UITextField文本更改事件


563

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

3
这是一个有1000票SO答案的好例子,这是完全错误的。iOS十分棘手,琐,充满了糊涂。
Fattie

Answers:


1056

正确的方式做uitextfield文本更改回调

我捕获了发送到UITextField控件的字符,如下所示:

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

在Objective-C中:

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

在Swift中:

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

然后,在该textFieldDidChange方法中,您可以检查textField的内容,并根据需要重新加载表格视图。

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


18
这是更好的解决方案-因为您也可以在Nibs或Storyboard中进行设置,而不必写出过多的UITextFieldTextDidChangeNotification代码
PostCodeism

3
唯一的问题是。它对我不起作用-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replaceString:(NSString *)string
iWheelBuy 2013年

4
@iWheelBuy仅当它返回时NO,这是合乎逻辑的,因为NO从该方法返回时,您基本上是在说字段中的文本不应更改。
gitaarik 2014年

2
这对于编辑引起的更改非常有用。它无法捕获通过发生的程序化更改textField.text = "Some new value";。有什么聪明的方法可以捕捉到这一点吗?
约翰

10
您可能会以为Apple 会考虑包含UITextFieldDelegate类似内容func textField: UITextField, didChangeText text: String,但是... (给Apple的家伙一个肮脏的样子)
Brandon A

394

XenElement的答案很明确。

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

UITextField更改事件


2
可能很容易,但是经过10到15分钟的搜索,直到偶然碰到您的答案,我才发现这种可能性。还有我的+1;使基于代码的解决方案和基于IB的解决方案在此类问题上获得高票是很好的。
Mark Amery

我刚刚在6.3中进行了检查,它就在那里。这是一个UITextView,右键单击它,然后查看“编辑更改”。
William T.

4
为什么在地球上它被命名为Edited Changed?狂!尽管感谢您的解决方案:-)
malhal 2015年

3
因为在编辑更改时将调用该事件,而“值更改”则在文本字段完成编辑即退出第一响应者时发生。UITextFieldTextDidChangeNotification是一个UITextField通知,而UIControlEventValueChanged由UIButton子类化的UIControl实现。
Camsoft

2
我注意到这不起作用,因为用户选项卡超出了文本字段,并且自动
更正的

136

设置事件监听器:

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

实际听:

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

凉。我喜欢这种方法,因为我想在已经是TextFieldDelegate的ViewController基类中实现textField-change-listening。这样,我就可以像魅力一样为(子视图中的textField)添加Target:baseControllerMethod。谢谢!
HBublitz

87

迅速:

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

然后,实现回调函数:

@objc func textFieldDidChange(textField: UITextField){

print("Text changed")

}

2
为我工作,我已经尝试过了,但是没有意识到该函数需要textField参数。如果这对您不起作用,请确保检查选择器是否具有要传递的textField参数(即使您不使用它也是如此)。
werm098

在textFieldDidChange中的textField之前添加_,如下所示:func textFieldDidChange(textField:UITextField){...}
Makalele,2017年

30

如此处所述:UITextField文本更改事件从iOS 6(已检查iOS 6.0和6.1)开始,似乎无法UITextField仅通过观察来完全检测对象的更改UITextFieldTextDidChangeNotification

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

我不知道这是错误还是故意的。似乎是一个错误,因为我在文档中找不到任何合理的解释。这里也说明了这一点:UITextField文本更改事件

我对此的“解决方案”是实际上对我所做的每项更改UITextField(如果更改是在不使用内置iOS键盘的情况下进行的)自己发布的。像这样:

myUITextField.text = @"I'm_updating_my_UITextField_directly_in_code";

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

[NSNotificationCenter.defaultCenter 
  postNotification:myTextFieldUpdateNotification];

这样,您100%确信更改对象的.text属性时会收到相同的通知UITextField,无论是在代码中“手动”更新对象还是通过内置的iOS键盘进行更新。

重要的是要考虑到,由于这不是已记录的行为,因此此方法可能会导致收到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键盘进行编辑时UITextField,并非每次键入/删除新字母都会更新文本字段的“ text”属性。相反,在您退出文本字段的第一响应者状态后,文本字段对象将“作为一个整体”进行更新。


5
为什么要自己解决这个问题,只需使用XenElement答案中的target-action方法。如果您需要数十行代码,则要做一些“应该”简单的事情,那么您可能做错了。
jrturton

6
这似乎是预期的行为。没有其他委托方法(例如,滚动,选择)调用由代码引发的事件。
jrturton

1
请注意,不能保证UIKit兼容KVO,因此KVO解决方案不一定会起作用。

@jrturton,关于您的“为什么”问题,在某些其他类(也许是装饰,动画等)中,您需要对文本字段中发生的事情做出响应是完全司空见惯的。
Fattie

27

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

在此处输入图片说明


14

在这里为相同的快速版本。

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

func textFieldDidChange(textField: UITextField) {

}

谢谢


12

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

3
该解决方案的问题是:将Cursor位置设置为字段的END(一旦执行textField.text = ...)。因此,如果用户想在该字段的中间编辑字符,则每次击键时,他们的光标将移至该字段的末尾。试试看。
FranticRock

@Alex好点,但是很容易解决。只需在本地NSString中计算结果值,然后将其传递给didChangeTextInTextField:toText:方法,然后返回YES,即可让iOS执行更新。
jk7

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

}

7

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

let textField = UITextField()

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

使用类似的类:

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

    }
}

4

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

希望能帮助到你。


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

}

最终,您会运行。


2

Swift 3.1:

选择器:ClassName.MethodName

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

  func fieldChanged(textfieldChange: UITextField){

    }

2

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(“最后一个文本字段已更改!”)可以帮忙吗?
Saeed Rahmatolahi

首先为您的textField创建Iboutlet。然后像我在答案中显示的那样设置委托和addTarget。在委托函数textFieldDidChange中,添加以下内容:“如果textField == yourLastTextField {print(“最后一个文本字段已更改!”)}现在我无法访问我的计算机。我将使此注释更具可读性进入我的电脑
约瑟夫·弗朗西斯

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
 }

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

    }

}

(提示-别忘了超级调用必须先!post调用。)

没有解决方案可用,除非你确定通过继承如图所示略高于控制。那是唯一的解决方案。

注意通知

UITextView.textDidChangeNotification

结果是

func textViewDidChangeSelection(_ textView: UITextView) 

被呼叫。

不是 textViewDidChange。)


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



- (void) didChangeTextViewText {
 //do something
}

0

一件事是您可能有多个UITextField。因此,给他们一个标签,然后您可以打开标签。这几乎是在任何类中设置观察者的方法。

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版本

使用键值观察 通知对象其他对象的属性更改。

var textFieldObserver: NSKeyValueObservation?

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

好主意。您可以将其包装在上下文中并让我们知道吗?
Revanth Kausikan '19年

1
不幸的是,这是完全错误的。志愿不能在iOS中工作的控件: stackoverflow.com/a/6352525/1402846 developer.apple.com/library/archive/documentation/General/...
Fattie
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.