UITextField with 2 delegates - iphone

I just made a formatter class that will automatically format numbers typed into a uitextfield and give back the correct format.
i.e.
Text field will look like this $0.00
if you type 1,2,3,a,b,c you get $1.23 in the text field.
I did this by making a custom class that was a UITextfieldDelegate and responded to the textfields delegate methods.
However my viewcontroller also needs to respond to when the text changes in this text field.
Can I have to delegates? Or am I going to have to make my formatter class have a delegate method also?

The way I solved this was to make a class method that would return the correctly formatted the string.
Then I kept the delegation to the viewController.
When it asks should the text field change. I simply set the text using my class method and then return no so that it essentially ignores the users input.

I also propose that you use a NSNotificationCenter for such a situation since you can't use two delegates, and here is an example of how to use NSNotificationCenter.

No, you can't have two delegates at once, it's one property, if you assign it for the second time, the first delegate will stop being a delegate. What you could do is make a common delegate class, where you set up an NSNotificationCenter to send notifications corresponding to the UITextField's events, and then register all your classes (which have to receive these events) to the NSNotificationCenter.

I realize I am a little late to this party, but why not just add the CustomDelegate class to your VC in Interface Builder and set the delegate for the UITextField to that? No extra code in the VC is required, no UITextField subclassing is needed either. Project link below that does this to custom format a phone number.
Link to example project This link will go away when I decide it will go away. It could live on here forever (or as long as Dropbox is in business).
Added code example of the delegate class (although if someone had any amount of experience in iOS development, this wouldn't be needed.) That screenshot explains just about all of the answer. Example project link added as a convenience.
Code from the Delegate class:
Header File:
#import <Foundation/Foundation.h>
#interface PhoneNumberFormatterDelegate : NSObject <UITextFieldDelegate>
#end
Implementation File:
#import "PhoneNumberFormatterDelegate.h"
#implementation PhoneNumberFormatterDelegate
#pragma mark - My Methods
-(void)textFieldDidChange:(UITextField *)textField {
if ([textField.text length] > 11) {
[textField resignFirstResponder];
}
}
#pragma mark - UITextField Delegate Methods
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
// If user hit the Done button, resign first responder
if([string isEqualToString:#"\n"]){
[textField resignFirstResponder];
return NO;
}
// All digits entered
if (range.location == 12) {
[textField resignFirstResponder];
return NO;
}
// Reject appending non-digit characters
if (range.length == 0 &&
![[NSCharacterSet decimalDigitCharacterSet] characterIsMember:[string characterAtIndex:0]]) {
return NO;
}
// Auto-add hyphen before appending 4rd or 7th digit
if (range.length == 0 &&
(range.location == 3 || range.location == 7)) {
textField.text = [NSString stringWithFormat:#"%#-%#", textField.text, string];
return NO;
}
// Delete hyphen when deleting its trailing digit
if (range.length == 1 &&
(range.location == 4 || range.location == 8)) {
range.location--;
range.length = 2;
textField.text = [textField.text stringByReplacingCharactersInRange:range withString:#""];
return NO;
}
return YES;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField {
[textField addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
[textField removeTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
}
#end

Related

How to make UITextField's text selected programmatically

I want to select all text from the UITextField selected when i start editing. I tried the below code but this doesn't work.
[txt selectAll:self];
Please check where you have placed it... Try putting the code above in
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
[textField selectAll:self];
}
And also txt must be UITextField. Also do not forget to set the delegate for txt as
txt.delegate = self;
where you have declared it and add UITextFieldDelegate in .h as
#interface ViewController : UIViewController <UITextFieldDelegate>
This will definitely work....It worked for me..
Xyz answered correctly, but you don't necessarily need to use delegate. You may just connect action from interface builder on "Editing did begin" and write same code there.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
NSString * str = [textField.text stringByReplacingCharactersInRange:range withString:string];
return YES;}
when you start editing use this code to store it ur string

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. :)

enabling UIBarButtonItem when textfield has input

I am trying to disable my send button (UIBarButtonItem within a toolbar) whenever there is no input in the "userInput" UITextField, and enable it when there is. Here is the code I've written - I can't quite figure out why it isnt working.
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (userInput.text.length >0) {
[sendButton setEnabled:YES];
}else{
[sendButton setEnabled:NO];
}
}
Im also getting a warning that says "control reaches end of non-void function."
Im very new to xcode and programming so I'm not sure what that is telling me.
Thanks for any help!!
The text field object sends the message shouldChangeCharactersInRange to its delegate asking whether the change (adding the new character or removing a character) should be permitted or not. Since you don't want to refuse any changes made to the text field itself, you must return YES from this method. The warning
control reaches end of non-void function
means you have a non-void function so you are supposed to return something. Since nothing was being returned, this message popped up.
One important thing to remember is that this delegate method is called before the change is made, which makes sense because the delegate is being asked for permission to allow or disallow the change. If the delegate refuses, the text field's value will remain the same regardless of what the user types.
So calling userInput.text is useless because it will give the old value back as the change hasn't been made yet. However, there is enough information in the parameters of this method to construct the new to-be value of the text field.
NSString *newText = [textField.text stringByReplacingCharactersInRange:range withString:string]);
The complete method would look like,
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *newText = [textField.text stringByReplacingCharactersInRange:range withString:string];
BOOL isFieldEmpty = [newText isEqualToString:#""];
sendButton.enabled = !isFieldEmpty;
return YES;
}
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (userInput.text.length >0) {
[sendButton setEnabled:YES];
}else{
[sendButton setEnabled:NO];
}
return YES;
}
ok you need in set enable NO in vieWillAppear
-(void)viewWillAppear:(BOOL)animated
{
[self.sendButton setEnabled:NO];
}
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (userInput.text.length >0) {
[self.sendButton setEnabled:YES];
}else{
[self.sendButton setEnabled:NO];
}
return YES;
}
make UIBarButton as property and IBOUtlet and make proper connection
.h
UIBarButton *sendButton;
#property(nonatomic,retain) IBOutlet UIBarButton *sendButton;
and in .m
#synthesize sendButton;
and
-(void)dealloc
{
[self.sendButton release];
[super dealloc];
}
and also remember to make connection for textField from IB.
In your controller add (weak) outlets for both the UITextView and UIBarButtonItem and hook them up to the items in view.
Add another IBAction to controller for example:
- (void)textChanged:(id)sender
{
if ( [self.someTextField.text length] > 0 )
[self.someBarButtonItem setEnabled:YES];
else [self.someBarButtonItem setEnabled:NO];
}
Hook up UITextView's Edit Changed event to the textChanged: you've just created.
The Edit Changed even fires up when characters are typed into UITextField, you check the length of the text and enable/disable UIBarButtonItem when the text field has some text.
You can also in viewDidLoad: check the initial text length and enable/disable the button depending on the text.
Hope that helps.
It works for me in iOS 6.1.

Best way to use "Next" version of Return button on UITextField to move to next UITextField

I use the "Next" value for the "Return Key" to get the Next button in place of the Done button, but (obviously) pressing it doesn't automatically move to the next UITextField in my view.
What's the right way to do this? On a larger topic, what are some tips for properly building forms in the iPhone SDK?
Make some object the first text field's delegate, and implement the - (BOOL)textFieldShouldReturn:(UITextField *)textField method; in that, call the second text field's -becomeFirstResponder. Returning YES from that will make the text field perform its default behavior for the return button – I think that's generally sending its action message. If you don't have anything added as a target of that action, it doesn't really matter what you return.
To build on Noah's answer, if you have a lot of textfields and don't feel like having a bunch of if's, you could do it this way:
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
//[[self.view viewWithTag:textField.tag+1] becomeFirstResponder];
UIView *view = [self.view viewWithTag:textField.tag + 1];
if (!view)
[textField resignFirstResponder];
else
[view becomeFirstResponder];
return YES;
}
Once you tag every textfield starting at any number, as long as they're tagged sequentially, in storyboard or in code, it should work.
For Swift:
func textFieldShouldReturn(textField: UITextField) -> Bool {
//your collection of textfields
guard let i = textFields.indexOf(textField) else { return false }
if i + 1 < textFields.count {
textFields[i + 1].becomeFirstResponder()
return true
}
textField.resignFirstResponder()
return true
}
This seems to work quite well and doesn't require the tag system many are suggesting. There are 2 things to note with this solution though:
All the UITextFields must be in the same UIView (have the same superview).
The UITextFields need to be in the right order in the interface builder.
-(BOOL)textFieldShouldReturn:(UITextField *)textField {
/*
* 1. Loop through the textfield's superview
* 2. Get the next textfield in the superview
* 3. Focus that textfield
*/
UIView *superView = [textField superview];
BOOL foundCurrent = false;
for (UITextField *tf in superView.subviews) {
// Set focus on the next textfield
if (foundCurrent) {
[tf becomeFirstResponder];
return NO;
}
//Find current textfield
if ([tf isEqual:textField]) {
foundCurrent = true;
}
}
return YES;
}
I don't like to deal with tag so here is my solution. Create an IBOutletCollection of all your textFields in your ViewController, drag to connect your textFields in order from top to bottom.
#interface ViewController () <UITextFieldDelegate>
#property (strong, nonatomic) IBOutletCollection(UITextField) NSArray *allTextFields;
#end
In viewDidLoad set your textFields delegate. (Or set it in storyboard).
for (VVTextField *tf in self.allTextFields) {
tf.delegate = self;
}
Then implement UITextField Delegate
#pragma mark - UITextField Delegate
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
NSUInteger currentIndex = [self.allTextFields indexOfObject:textField];
NSUInteger nextIndex = currentIndex+1;
if (nextIndex < self.allTextFields.count) {
[[self.allTextFields objectAtIndex:nextIndex] becomeFirstResponder];
} else {
[[self.allTextFields objectAtIndex:currentIndex] resignFirstResponder];
}
return YES;
}
I've been struggling with this issue too...and as a result I've created small library for handling multiple textfields. You can find it on github GNKeyboardAwareScrollView#GNTextFieldsManager.
You can initialise it with array of textfields:
NSArray *myTextFields = #[...]; // the order of array matters!
GNTextFieldsManager *manager = [[GNTextFieldsManager alloc] initWithTextFields:myTextFields];
Or by specifying parent view (and setting tags for all views):
GNTextFieldsManager *manager = [[GNTextFieldsManager alloc] initWithView:self.view];
Hope i'll be useful for somebody :)

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>