6

Using the notification center, as described in How can I get a textDidChange (like for a UISearchBar) method for a UITextField?, allows you to get a notification when text in an iOS UITextField changes. I didn't see a similar notification for selection changes. What's the best way to detect and respond to changes in the selection for a UITextField?

Community
  • 1
  • 1
Mike
  • 3,084
  • 1
  • 25
  • 44

4 Answers4

0

This link has one solution without messing with private apis:

In short: Subclass UITextField, implement

-(BOOL)canPerformAction:(SEL)action withSender:(id)sender

and check if the selection range has changed. But this does not notify about taps.

So what I did is after subclassing, observe the selectedTextRange property:

-(void)awakeFromNib {
    [self addObserver:self forKeyPath:@"selectedTextRange" options:0 context:nil];
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    [self notifyDelegateIfNecessary];
}

Works very well in iOS8.

Krisdigital
  • 630
  • 1
  • 8
  • 16
  • 1
    why you don't override [UITextField setSelectedTextRange:(UITextRange *)selectedTextRange] if you anyway did subclass UITextField? Then all changes come along in this place. – Leo Jun 13 '16 at 14:33
  • @Leo 1. I do not want to change the behavior, only get notifications 2. I just checked in PlayGround and I think you cannot override that function. – Krisdigital Jun 14 '16 at 11:51
  • It works for me (IOS 8+9). This way I surround the needs to remove the observer at the suitable place. – Leo Jun 14 '16 at 12:12
  • Your choice, I would not override a private method, but probably a thing of taste. – Krisdigital Jun 14 '16 at 13:24
  • 1
    @Krisdigital if you override *setSelectedTextRange* and in its implementation call *[super setSelectedTextRange:text]*, you'll maintain the original behavior of the superclass but also get a notification point – nalexn Nov 24 '16 at 16:34
  • @NAlexN I know, feel free to override to your liking ;) – Krisdigital Nov 27 '16 at 16:13
0

There is simple way to detect selection in textField

add object property observer in viewDidAppear

  [self.keyboardInputFieldPassword addObserver:self forKeyPath:@"selectedTextRange" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld  context:nil];

Then Add observe function for property

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if([keyPath isEqualToString:@"selectedTextRange"] && self.keyboardInputFieldPassword == object)
        [self textFieldDidChangeSelection:self.keyboardInputFieldPassword];
}

This will capture the selection range in UITextField

For following the convention, you should removeObserver in viewDidDisappear

[self.keyboardInputFieldPassword removeObserver:self forKeyPath:@"selectedTextRange" context:nil];
Arup Sarker
  • 173
  • 1
  • 9
0

Swift 4

private func listenForSelection(in textField:UITextField) {
    textField.addObserver(self, forKeyPath: "selectedTextRange", options: [.new,.old], context: nil)
}
private func unlistenForSelection(in textField:UITextField) {
    textField.removeObserver(self, forKeyPath: "selectedTextRange")
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    let textField = object as? UITextField
    assert(keyPath == "selectedTextRange")
    assert(textField != nil)
    // select everything but the first and last characters - just and example of the UITextInput API if you want to modify the selection
    if let startPosition = textField.position(from: textField.beginningOfDocument, offset: 1),
        let endPosition = textField.position(from: textField.endOfDocument, offset: -1) {
        let selectionRange = textField.textRange(from: startPosition, to: endPosition)
        textField.selectedTextRange = selectionRange
    }
}
bshirley
  • 8,217
  • 1
  • 37
  • 43
-5

Wire up a method using target/action to the UIControlEventEditingChanged control event on your instance of UITextField. This can be done in the interface builder or in code by adding a target/action pair to the text field and then implementing the specified method.

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

UITextField also declares a UITextFieldDelegate protocol with methods that you can implement in your class to be notified of changes to an instance of UITextField. I recommend that you become comfortable with delegation as it is a common pattern in the Cocoa framework.

UITextFieldDelegate Class Reference

Mark Adams
  • 30,776
  • 11
  • 77
  • 77
  • 4
    [`UIControlEventEditingChanged`](http://developer.apple.com/library/ios/documentation/uikit/reference/UIControl_Class/Reference/Reference.html#//apple_ref/doc/c_ref/UIControlEventEditingChanged) only refers to text edits. I.e., the action only fires when the text changes while the text field `isEditing`. This doesn't include selection changes. – ma11hew28 Jul 06 '12 at 02:38