4

I have several UITextView subviews, all using the same custom input interface (basically a numberpad with an autofill-option and a save button).

My problem is that the delegate method shouldChangeCharactersInRange: is not called when the textfield's text is modified from my custom keyboard (it does work when pasting text from clipboard into the textfields and also when using the standard numberpad keyboard). The text of the textfields change, but the delegate method to prevent invalid entries is not called. Other delegate methods of style DidBeginEditing: are called always.

despite of what is said in this SO LINK the documentation states that the shouldChangeCharactersInRange: delegate method will be called: "The text view calls this method whenever the user types a new character or deletes an existing character."

What am I missing?

relevant code parts:

ViewController.h:

@interface ManualPositionViewController : UIViewController <UITextFieldDelegate> {
    LocationEntryTextField *latitude;
}
@property (nonatomic, retain) IBOutlet LocationEntryTextField *latitude;
@property (nonatomic, retain) IBOutlet LocationKeyboard *locationKeyboard;
..

ViewController.m:

@synthesize latitude;
@synthesize locationKeyboard;
self.latitude.inputView = locationKeyboard;
self.latitude.delegate = self;

- (void)textFieldDidBeginEditing:(LocationEntryTextField *)aTextField {

    NSLog(@"textFieldDidBeginEditing called!");
    self.locationKeyboard.currentTextfield = aTextField;
}

- (BOOL)textField:(LocationEntryTextField *)editedTextField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)replacementString {

    NSLog(@"shouldChangeCharactersInRange called!");
    NSCharacterSet *decimalSet = [NSCharacterSet decimalDigitCharacterSet];

    if ([[replacementString stringByTrimmingCharactersInSet:decimalSet] isEqualToString:@""]) { 
        NSLog(@"Result: YES");
        return YES;
    }
    else {
        NSLog(@"Result: NO");           
        return NO;
    }
}

LocationKeyboard.h:

#import <UIKit/UIKit.h>
#import "LocationEntryTextField.h"

@interface LocationKeyboard : UIView {
    LocationEntryTextField  *currentTextfield; // track first responder
}
@property (weak) LocationEntryTextField *currentTextfield;
- (IBAction) numberButtonPressed:(UIButton*)sender;
- (IBAction) backspaceButtonPressed:(UIButton*)sender;
@end

- (IBAction) numberButtonPressed:(UIButton*)sender {
    NSString *entryString = @"test";
    [self.currentTextfield replaceRange:self.currentTextfield.selectedTextRange withText:entryString];
}

LocationEntryTextField.h:

@interface LocationEntryTextField : UITextField
..
Community
  • 1
  • 1
user1702623
  • 235
  • 3
  • 6

1 Answers1

8

This line:

[self.currentTextfield replaceRange:self.currentTextfield.selectedTextRange withText:entryString];

doesn't result in a call to textField:shouldChangeCharactersInRange:replacementString:. Is that what you are expecting?

Since you are explicitly changing the text of the text field, there is no "typing" going on.

The proper way to have your custom keyboard update the text field is to call the 'insertText:` method. This method will properly deal with any selection, moving the cursor, and calling delegate methods.

Edit: You may wish to look at my answer here for a complete custom keyboard setup (minus the actual buttons).

Community
  • 1
  • 1
rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • Hi rmaddy, I just tried the insertText: method, but the result remains the same. My delegate method is only called when pasting, cutting text from the pasteboard but not when typing from keyboard. I adopted your keyboard as well (nice idea to trace the first responder that way!), but still no improvement. The textField:shouldChangeCharactersInRange:replacementString: is not called. Finally I'm using the method described [here](http://dev.ragfield.com/2009/09/insert-text-at-current-cursor-location.html), but it's what I wanted. – user1702623 Nov 09 '12 at 17:20
  • Sorry, you are right. I could have sworn it was being called in my own app but I just doubled check - it's not. Looks like I need to update my code to explicitly call the delegate method before calling `insertText`. – rmaddy Nov 09 '12 at 17:49
  • I tried this as well. It's not easy because you need to know where the cursor is and if text is marked to fill the NSRange argument, but the UITextField.selectedTextRange is of type UITextRange which consists of two UITextPosition (start, end) which is a abstract class. I could not find a way to convert this into NSRange. – user1702623 Nov 09 '12 at 20:01
  • I updated the code in my other answer. The updated code now properly handles calling the "shouldChange" method. Have a look. – rmaddy Nov 09 '12 at 20:09