关于ios:UITextField文本更改事件

UITextField text change event

如何检测文本字段中的任何文本更改?委托方法shouldChangeCharactersInRange可以用于某种用途,但它不能完全满足我的需要。因为在返回Yes之前,文本字段文本不可用于其他观察者方法。

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

是他们获取EDCOX1,2,Java事件处理程序的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (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;
}

从正确的方式执行uitextfield文本更改回拨:

I catch the characters sent to a UITextField control something like this:

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

在Objul-C中:

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

在SWIFT中:

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

Then in the textFieldDidChange method you can examine the contents of the textField, and reload your table view as needed.

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


Xenelement的答案是"当场"。

在Interface Builder中,也可以通过右键单击uitextfield并将"编辑已更改"发送事件拖动到子类单元来完成上述操作。

UITextField Change Event


要设置事件侦听器:

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

要真正倾听:

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


Swift:

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

然后,实现回调函数:

1
2
3
4
5
@objc func textFieldDidChange(textField: UITextField){

print("Text changed")

}


如前所述:uitextfield文本更改事件,似乎在iOS 6(iOS 6.0和6.1已检查)中,仅通过观察UITextFieldTextDidChangeNotification就无法完全检测UITextField对象中的更改。

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

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

我对此的"解决方案"是,实际上将我所做的每一个更改都由我自己发布一个通知给我的UITextField(如果在不使用内置iOS键盘的情况下完成了该更改)。像这样:

1
2
3
4
5
6
7
8
myUITextField.text = @"I'm_updating_my_UITextField_directly_in_code";

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

[NSNotificationCenter.defaultCenter
  postNotification:myTextFieldUpdateNotification];

这样,无论您是在代码中"手动"更新还是通过内置的iOS键盘,当您更改UITextField对象的.text属性时,您都完全有信心收到相同的通知。

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

稍有不同的方法是发布自定义通知(这是一个自定义名称,而不是UITextFieldTextDidChangeNotification),如果您实际上需要知道通知是您的还是"iOS生成的"。

编辑:

我刚刚找到了一种我认为更好的方法:

这涉及到Objective-C的关键值观察(kvo)功能(http://developer.apple.com/library/ios/documentation/cocoa/conceptive/keyValueObservation/keyValueObservation.html//apple-ref/doc/uid/1000017-bcicjdha)。

基本上,您将自己注册为一个属性的观察者,如果这个属性发生了变化,您将得到关于它的通知。"原则"与NSNotificationCenter的工作原理非常相似,这是自iOS 6起这种方法自动工作的主要优势(不需要任何特殊的调整,例如必须手动发布通知)。

对于我们的UITextField方案,如果您将此代码添加到(例如)包含文本字段的UIViewController中,这会很好地工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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的过程中,文本字段的"文本"属性不会随着键入/删除的每个新字母而更新。相反,在您放弃文本字段的第一响应者状态后,文本字段对象将"整体"更新。


我们可以很容易地从Storyboard配置,ctrl拖动@IBAction并更改事件如下:

enter image description here


这里有一个同样的Swift版本。

1
2
3
4
5
textField.addTarget(self, action:"textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)

func textFieldDidChange(textField: UITextField) {

}

谢谢


我解决了改变shouldchangechractersinrange行为的问题。如果返回"否",则iOS不会在内部应用更改,而是有机会手动更改更改,并在更改后执行任何操作。

1
2
3
4
5
6
7
- (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
}


测试的Swift版本:

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

参数说明:

1
2
3
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中添加您上面创建的方法:

1
2
3
4
5
6
//Gets called everytime the text changes in the textfield.
func textFieldDidChange(textField: UITextField){

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

}

斯威夫特3:

1
2
3
4
5
6
7
let textField = UITextField()

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

使用类如:

1
2
3
4
5
class MyClass {
    func textChanged(sender: Any!) {

    }
}

斯威夫特4

1
2
3
4
5
6
7
8
9
10
11
12
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!)

}


斯威夫特3版

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

把零钱拿进来

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

希望它有帮助。


斯威夫特3.1:

Selector: ClassName.MethodName

1
2
3
4
5
  cell.lblItem.addTarget(self, action: #selector(NewListScreen.fieldChanged(textfieldChange:)), for: .editingChanged)

  func fieldChanged(textfieldChange: UITextField){

    }

关闭:

1
2
3
4
5
6
7
8
9
10
11
   class TextFieldWithClosure: UITextField {
    var targetAction: (() -> Void)? {
        didSet {
            self.addTarget(self, action: #selector(self.targetSelector), for: .editingChanged)
        }
    }

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

并使用

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

您应该使用通知来解决这个问题,因为另一个方法会监听输入框而不是实际输入,特别是当您使用中文输入法时。在视图加载中

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

然后

1
2
3
4
5
6
7
8
9
10
11
12
- (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;
}

}

最后,你跑,会成功的。


1
2
3
4
5
6
7
8
9
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didChangeTextViewText:)
name:UITextFieldTextDidChangeNotification object:nil];



- (void) didChangeTextViewText {
 //do something
}


Swift 3版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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

   }

}

别忘了设置委托。


斯威夫特4版

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

1
2
3
4
5
6
var textFieldObserver: NSKeyValueObservation?

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


观察者和反应式swift(rxcococa&rxswift)非常简单。

只需订阅RX的文本属性,如下所示:

1
2
3
myTextField.rx.text.subscribe { text in
   print("UITextFieldTextChangeEvent Text:\(text)")
}.disposed(by: disposeBag)