textField:shouldChangeCharactersInRange:replacementString: in subclass - iphone

I've subclassed a UITextField and I'd like to use a method similar to textField:shouldChangeCharactersInRange:replacementString: inside the subclass to get notified when a character is typed and if necessary block the change, but avoid setting the fields delegate to itself.
I've found if I override keyboardInput:shouldInsertText:isMarkedText: and keyboardInputShouldDelete: I can get the desired effect, unfortunately these methods are private and anything using the class won't make it through the App Store submission process.
Anyone know of a public method that achieves the same thing and doesn't require the field being its own delegate?
UPDATE:
I went with the suggestion of creating a separate object just to be the delegate, which itself can have a delegate to forward messages to.

Having tried to subclass UITextField before, I've since learned to avoid that and go the delegate route instead (it has a similar method called - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string that should do what I think you've described).

One strategy to explore here is to override the setDelegate: method and then do some message forwarding. You can use [super setDelegate: self] to make sure your calls gets first dibs at the delegate messages. In your override of setDelegate: set an internal ivar, e.g.
- (void) setDelegate: (id<UITextFieldDelegate>) internalDelegate;
{
[setInternalDelegate: internalDelegate];
}
Then for each of the the UITextField delegate methods do your thing before forwarding on the delegate message, e.g.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string;
{
// do your thing with range and replacement string
// now forward message on the 'other' delegate
[[self internalDelegate] textField: self shouldChangeCharactersInRange: range replacementString: string];
}
Typically, you'll want to override all of the delegate methods, even if for most of them all you do is a straight forward.
Update You note in comments that the forwarding approach is raising issues. If so, then traditional delegation is the way to go. (And, in generally, it is the way to go - although I've used forwarding delegate once or twice, I'm not sure if, with hindsight, it was absolutely essential and I've not checked to see if I've done it with UITextField. #Scott Corscadden has and does not recommend it.)
The most common pattern is to make the ViewController that looks after the view in which the UITextField is a subview the delegate. You don't say in your answer if there's particular reason why you need to work with the subclass. If you're packing interesting stuff into the UITextField then it might be, though you could always another poster suggests and create a companion class for UITextField that does that work and use that as the delegate. In any case, if need be, you can always get the delegate object to call extra methods on your UITextField subclass, e.g.
// in the delegate object class
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string;
{
[delegate doSomeExtraThingInTheTextFieldSubclassThatItSeemsToMakeSenseToDoThereRatherThanHere];
// maybe that's it, or maybe this object also wants to do something here...
}

You may play with Objective-C runtime to achieve this. The following code is working for me.
The idea is:
in subclass we set delegate property to self;
we override setDelegate: method in which we store supplied delegate to a variable (forwardDelegate);
by overriding respondsToSelector:, forwardInvocation: and methodSignatureForSelector: we either call method on self or forward it to saved forwardDelegate.
// MyTextField.h
#interface MyTextField : UITextField
#end
// MyTextField.m
#interface MyTextField () <UITextFieldDelegate>
#end
#implementation MyTextField {
id forwardDelegate;
}
- (void) myTextFieldCommonInit
{
[super setDelegate:self];
}
- (id) initWithCoder:(NSCoder *)coder
{
if ((self = [super initWithCoder:coder])) {
[self myTextFieldCommonInit];
}
return self;
}
- (id) initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
[self myTextFieldCommonInit];
}
return self;
}
- (void) setDelegate:(id)delegate
{
forwardDelegate = delegate;
}
- (BOOL) respondsToSelector:(SEL)selector
{
if ([super respondsToSelector:selector]) {
return YES;
} else {
return [forwardDelegate respondsToSelector:selector];
}
}
- (void) forwardInvocation:(NSInvocation *)invocation
{
if ([super respondsToSelector:[invocation selector]]) {
[super forwardInvocation:invocation];
} else if ([forwardDelegate respondsToSelector:[invocation selector]]) {
[invocation invokeWithTarget:forwardDelegate];
} else {
[self doesNotRecognizeSelector:[invocation selector]];
}
}
- (NSMethodSignature *) methodSignatureForSelector:(SEL)selector
{
NSMethodSignature *signature = [super methodSignatureForSelector:selector];
if (signature) {
return signature;
} else {
return [forwardDelegate methodSignatureForSelector:selector];
}
}
#pragma mark - UITextFieldDelegate
- (BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
...
}
#end

You can use the delegate method of the uitextfield,
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
}
And don't forget to set your textfield.delegate = self in ViewDidLoad.

Related

Subclass of UITextField

I want to subclass UITextField and have a method that sets the maximum number of characters. allowed. In the init method of this subclass I try setting the inputDelegate to self but the callbacks aren't triggered. Also this is an iOS 5 thing only. My question is what protocol or notification would allow me to fire an event when a key is tapped so that I don't have to implement the UITextField delegate in my view controller to achieve this limit.
Thanks.
You could do it like this, create a delegate class for you textfield
.m file
#implementation LimitedTextFieldDelegate
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if(textField.text.length >= 5 && ![string isEqualToString:#""])
return NO;
return YES;
}
#end
.h file
#interface LimitedTextFieldDelegate : UITextField<UITextFieldDelegate>
#end
in the UITextField subclass do the following
- (id)init
{
self = [super init];
if (self) {
//limitedDelegate is a property of your textfield subclass
self.limitedDelegate = [[LimitedTextFieldDelegate alloc] init];
self.delegate = self.limitedDelegate;
}
return self;
}

Hide keyboard/ resignFirstResponder forcibly

i'm working on an app which has a tableView with a textField in the right side of its each cell(there are more than 20 cells).
i've created custom cell's for each row except for the last one.
In the last row there is only a button.
Now i want to call resignFirstResponder on the button's click.
What should i do Please help?
You will have to keep track of which textfield in which cell has the first responder and resign it like this.
[myCellTextField resignFirstResponder];
You probably want to keep track of the text field with the keyboard. Implement the <UITextFieldDelegate> protocol in your controller, and set the controller as each of the text fields' delegates. Write the textFieldDidBeginEditing: method like so, setting an instance variable called currentTextField:
- (void)textFieldDidBeginEditing:(UITextField *)textField {
currentTextField = [textField retain];
}
Then, in your action for the button run [currentTextField resignFirstResponder].
Aopsfan's answer is probably the best solution so far. However, to add to it (as I cannot post comments), do remember to deallocate the object:
- (void)textFieldDidBeginEditing:(UITextField *)textField {
if (currentTextField != nil) {
[currentTextField release];
}
currentTextField = [textField retain];
}
Better still use #property's and #synthesize so the runtime can do the memory management for you.
[ViewController].h
#property (nonatomic, retain) UITextField* currentTextField;
[ViewController].m
#synthesize currentTextField = _currentTextField;
- (void)viewDidLoad|Appear {
self.currentTextField = nil;
}
- (void) dealloc {
[_currentTextField release], _currentTextField = nil;
...
[super dealloc];
}
- (void)textFieldDidBeginEditing:(UITextField *)textField {
self.currentTextField = textField;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (self.currentTextField) {
[self.currentTextField resignFirstResponder];
}
}
I think this link will help you:
Objective C: ResignFirstResponder with a button
Provide some code so that, it will be easier to help u.
Hope this helps you. :)

textFieldShouldBeginEditing not being called

I am trying to use textFieldShouldBeginEditing to disable the keyboard from showing up for a custom UITextField. I'm implementing all the UITextFieldDelegate methods. However, for some reason, textFieldShouldBeginEditing actually never gets called.
The following delegate methods ALWAYS get called:
– textFieldDidBeginEditing:
– textFieldShouldEndEditing:
– textFieldDidEndEditing:
The view is structured in the following way:
UIViewController which holds a scrollview. Depending on the state of the view, this ScrollView will contain a UIView with a list of custom UITextFields.
I'm running iOS 4.3.5 (8L1) on this device.
Any ideas?
Edit; added some code snippets:
UIViewController has the following interface
#interface AViewController: UIViewController<UITextFieldDelegate>
Once the UIViewController loads, I connect all UITextFields to the view using
aSubView.aTextField.delegate = self;
(Simplified) delegate implementations located in AViewController
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
}
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
return YES;
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
return YES;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
return YES;
}
Custom UITextField code
Simplified implementation file --
#import "PVEntryTextField.h"
#import "EntryViewController.h"
#implementation PVEntryTextField
#synthesize isPasswordField, state, singleTap;
- (id)initWithCoder:(NSCoder *)inCoder
{
if (self = [super initWithCoder:inCoder])
{
self.font = [UIFont fontWithName:#"Helvetica-Bold" size:19];
self.textColor = [UIColor colorWithRed:51.0/255.0
green:51.0/255.0
blue:51.0/255.0
alpha:1.0];
self.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
}
return self;
}
- (CGRect)textRectForBounds:(CGRect)bounds
{
return CGRectMake(bounds.origin.x + 16, bounds.origin.y,
bounds.size.width - 16*2 - 10, bounds.size.height);
}
- (CGRect) editingRectForBounds:(CGRect)bounds
{
return [self textRectForBounds:bounds];
}
- (BOOL) canBecomeFirstResponder
{
return YES;
}
- (void) updateState:(int) newState
{
state = newState;
}
- (void)dealloc
{
[super dealloc];
}
#end
Is it posible that textFieldShouldBeginEditing is called by the default implementation of the method canBecomeFirstResponder?
Try implementing the method by [super canBecomeFirstResponder] or just removing it.
Have you set the UITextField delegate to "self"?
For anyone that comes here and didn't find a solution. My problem was that I created the textField in IB and then alloc one in my viewDidLoad. When I removed the instantiation, the delegate worked correctly as it was tied to the correct TF.
//I REMOVED these two lines because I created the textfield in IB
_nameTextField = [[UITextField alloc] init];
_priceTextField = [[UITextField alloc] init];
[_nameTextField setDelegate:self];
[_priceTextField setDelegate:self];
I also ran into the issue of not having textFieldShouldEndEditing or textFieldShouldReturn called. This occurred after updating my app's Storyboard.
The delegate methods were getting called when the UITextFields where part of the ViewController subclass.
But, when they were moved into a Scroll View within the ViewController, the methods were no longer called.
Setting their app delegates to self in ViewDidLoad fixed this problem.
self.emailField.delegate = self;
self.passwordField.delegate = self;
In Swift-3 following method required "_" before textField so that this delegate method will call. In my case it helps me.
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
}
Dont know if is a typo error on copying the code here but the method name is incorrect:
CORRECT - textFieldShouldBeginEditing
YOURS - textFieldShoulBeginEditing (missing "d")

Textfield event is never called in delegate

In my interface I have:
#interface MainViewController : UIViewController <SKProductsRequestDelegate, SKPaymentTransactionObserver, UITextFieldDelegate> {
UITextField *Stock;
// ....
}
This is the implementation I have:
- (BOOL)Stock:(UITextField *)textField shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
if([[textField text] isEqualToString:#"\n"]) {
[textField resignFirstResponder];
return NO;
}
return YES;
}
In the viewDidLoad I have Stock.delegate = self;
I am expecting this method is called after any character is typed in the text field. But this routine is never called. What is the problem?
Thanks
If this is a UITextField, you've just implemented a random method. The actual delegate method is
-textField:shouldChangeCharactersInRange:replacementString:
Try providing that one.
Did you wire the TextField (Stock in your case) Delegate to FileOwner ?
If not then try this in viewDidLoad method of the Controller
Stock.delegate = self;
Or you can just wire it in IB.
Declare MainViewController conforming UITextFieldDelegate:
#interface MainViewController : UIViewController < UITextFieldDelegate >
To be called this method of UITextFieldDelegate should be declared as:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
In code or in IB MainViewController should be set to be delegate.
Correct and it will be fired as supposed.

Keyboard Iphone

Is possible to know when the user touch the keyboard iphone? When the user touch some button from keyboard... :/
The easiest way is to use a TextField. Even is your UI Does not call for one, you can set it's frame to zero so it doesnt show up onscreen. Then you can get access to the keys pressed by using the text field's delegate callback methods.
- (void)viewDidLoad {
[super viewDidLoad];
//CGRectZero because we don't want the textfield to be shown onscreen
UITextField *f = [[UITextField alloc] initWithFrame:CGRectZero];
//We set the delegate so we can grab keypressed
f.delegate = self;
[self.view addSubview:f];
[f becomeFirstResponder]; //Show the keyboard
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string {
if (string.length >0) {
NSLog(#"%# Pressed",string);
}
else {
NSLog(#"Backspcae pressed");
}
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
NSLog(#"return pressed");
return YES;
}
Note: to avoid a compiler warning, make sure in your .h file the class explicitly says it implements the UITextFieldDelegate protocal. ie:
#interface MyViewController : UIViewController <UITextFieldDelegate>