6

I am adding this Method to my code to format the textfield. I am using the code below to try and add the method, but it not working, what am I doing wrong?

.h file

NSString* phone_;
UITextField* phoneFieldTextField;
@property (nonatomic,copy) NSString* phone;

.m file

@synthesize phone = phone_;

ViewDidLoad{
self.phone = @"";
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];

// Make cell unselectable and set font.
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.textLabel.font = [UIFont fontWithName:@"ArialMT" size:13];

if (indexPath.section == 0) {

    UITextField* tf = nil;
    switch ( indexPath.row ) {
case 3: {
            cell.textLabel.text = @"Phone" ;
            tf = phoneFieldTextField = [self makeTextField:self.phone placeholder:@"xxx-xxx-xxxx"];
            phoneFieldTextField.keyboardType = UIKeyboardTypePhonePad;

            [self formatPhoneNumber:phoneFieldTextField.text deleteLastChar:YES];

            [cell addSubview:phoneFieldTextField];
            break ;
}
    // Textfield dimensions
    tf.frame = CGRectMake(120, 12, 170, 30);

    // Workaround to dismiss keyboard when Done/Return is tapped
    [tf addTarget:self action:@selector(textFieldFinished:) forControlEvents:UIControlEventEditingDidEndOnExit];

} 
}

// Textfield value changed, store the new value.
- (void)textFieldDidEndEditing:(UITextField *)textField {

//Section 1.
if ( textField == nameFieldTextField ) {
    self.name = textField.text ;
} else if ( textField == addressFieldTextField ) {
    self.address = textField.text ;
} else if ( textField == emailFieldTextField ) {
    self.email = textField.text ;
} else if ( textField == phoneFieldTextField ) {
    self.phone = textField.text ;
}else if ( textField == dateOfBirthTextField ) {
    self.dateOfBirth = textField.text ;
}

}

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString* totalString = [NSString stringWithFormat:@"%@%@",textField.text,string];

// if it's the phone number textfield format it.
if(textField.tag == 10 ) {
    if (range.length == 1) {
        // Delete button was hit.. so tell the method to delete the last char.
        textField.text = [self formatPhoneNumber:totalString deleteLastChar:YES];
    } else {
        textField.text = [self formatPhoneNumber:totalString deleteLastChar:NO ];
    }
    return false;
}

return YES;
 NSLog(@"Testing should change character in range"); 
}

-(NSString*) formatPhoneNumber:(NSString*) simpleNumber deleteLastChar:(BOOL)deleteLastChar {

if(simpleNumber.length == 0) return @"";
// use regex to remove non-digits(including spaces) so we are left with just the numbers
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[\\s-\\(\\)]" options:NSRegularExpressionCaseInsensitive error:&error];
simpleNumber = [regex stringByReplacingMatchesInString:simpleNumber options:0 range:NSMakeRange(0, [simpleNumber length]) withTemplate:@""];

// check if the number is to long
if(simpleNumber.length>10) {
    // remove last extra chars.
    simpleNumber = [simpleNumber substringToIndex:10];
}

if(deleteLastChar) {
    // should we delete the last digit?
    simpleNumber = [simpleNumber substringToIndex:[simpleNumber length] - 1];
}

// 123 456 7890
// format the number.. if it's less then 7 digits.. then use this regex.
if(simpleNumber.length<7)
    simpleNumber = [simpleNumber stringByReplacingOccurrencesOfString:@"(\\d{3})(\\d+)"
                                                           withString:@"($1) $2"
                                                              options:NSRegularExpressionSearch
                                                                range:NSMakeRange(0, [simpleNumber length])];

else   // else do this one..
    simpleNumber = [simpleNumber stringByReplacingOccurrencesOfString:@"(\\d{3})(\\d{3})(\\d+)"
                                                           withString:@"($1) $2-$3"
                                                              options:NSRegularExpressionSearch
                                                                range:NSMakeRange(0, [simpleNumber length])];

if (simpleNumber.length == 10 && deleteLastChar == NO) { [self resignFirstResponder];}

return simpleNumber;
NSLog(@"Testing format phone number"); 
}

#pragma mark - TextField
-(UITextField*) makeTextField: (NSString*)text
              placeholder: (NSString*)placeholder  {
UITextField *tf = [[UITextField alloc] init];
tf.placeholder = placeholder;
tf.text = text ;
tf.autocorrectionType = UITextAutocorrectionTypeNo ;
tf.autocapitalizationType = UITextAutocapitalizationTypeNone;
tf.adjustsFontSizeToFitWidth = YES;
tf.returnKeyType = UIReturnKeyDone;
tf.textColor = [UIColor colorWithRed:56.0f/255.0f green:84.0f/255.0f blue:135.0f/255.0f alpha:1.0f];
return tf ;
}
Community
  • 1
  • 1
Jason
  • 650
  • 2
  • 10
  • 33

4 Answers4

7

The method you are using:

-(NSString*) formatPhoneNumber:(NSString*) simpleNumber deleteLastChar:(BOOL)deleteLastChar

Returns an NSString Object. In your case you are calling the method correctly but you are not setting the Returned NSString object to anything. It is simply hanging there. You need to set the phoneFieldTextField to the formatted text like so:

phoneFieldTextField.text = [self formatPhoneNumber:phoneFieldTextField.text deleteLastChar:YES];

NOTE - If you want to learn more about return methods then read the following:

If you noticed some most methods are of the void type. You know this when you see a method like this:

- (void)someMethod {
    int x = 10;
}

What void means is that the someMethod does not return anything to you. It simply executes the code within the method. Now methods than return an object or some other data type look like this:

- (int)returnSomething {
    int x = 10;
    return x;
}

First thing you will notice is the return type is no longer void, it is an int. This means the method will return an integer type. In this case the code executes and you are returned the value of x.

This is just the start of the topic of return methods but hopefully it makes things a bit clearer for you.

dana0550
  • 1,125
  • 1
  • 9
  • 16
3

First off you need to tell us What is not working we don't have your app and all your code. You need to explain what is working and what is not working exactly. It took longer then necessary to figure out that your question is why is textField:shouldChangeCharactersInRange: not working. Did you set a breakpoint in the function to see what it was doing. Was it not being called?

That said your bug is that textField:shouldChangeCharactersInRange: is using tags to identify text fields but the rest of the code is using pointers

// if it's the phone number textfield format it.  
- if(textField.tag == 10 ) {
+ if(textField.tag == phoneFieldTextField ) {

Also you didn't include the code for makeTextField:placeholder: There could be issues in it too. Compare your code to the makeTextField:placeholder: in my sample.

I created a sample project on GitHub. To fix this. I also demos a better approach to creating input forms using table views.

https://github.com/GayleDDS/TestTableViewTextField.git

Look at both diffs to see what I did to YourTableViewController.m to make things work.

https://github.com/GayleDDS/TestTableViewTextField/commit/d65a288cb4da7e1e5b05790ea23d72d472564793 https://github.com/GayleDDS/TestTableViewTextField/commit/31ecaec8c9c01204643d72d6c3ca5a4c58982099

There is a bunch of other Issues here:

  • You need to call [super viewDidLoad]; in your viewDidLoad method
  • You need to correctly indent your code (could be a cut and paste issue)
  • You should be using the storyboard to create your views. See the better solution tab and BetterTableViewController implementation.

Must Watch - iOS Development Videos

GayleDDS
  • 4,443
  • 22
  • 23
  • First, my question in the title says exactly what is not working. Second, the "bug" is not in the `textField:shouldChangeCharactersInRange:` because it's not even my method, and Dana below was able to get it to work just fine. I set the textField.tag accordingly. I also logged what is being called. My case 3 is getting called just fine and everything works with the cell and the placeholder fine. – Jason May 19 '13 at 15:49
  • The issue is formatting the cell to use the phone number method I found on this site and referenced properly. `superView didLoad` is also in my `viewDidLoad` it's just not needed for this question. I am also using storyboards as you suggested. – Jason May 19 '13 at 15:50
  • 1
    Hi @Jason did you download and checkout my sample app? Ok your setting the tag did you also set the delegate? Can you also include your makeTextField code in your question above. Thanks – GayleDDS May 19 '13 at 16:14
  • I didn't set the delegate, dang it. Thank you!! – Jason May 19 '13 at 16:22
1

Looks like you are not setting the delegate <UITextFieldDelegate> in the .h file, and not assigning your textfield's delegate property to self tf.delegate = self; in order to call - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string

Try that and let me know how it goes

-Good Luck!

koray
  • 242
  • 1
  • 8
1

@koray was right: you need to setup the delegate for the class. Your class should be declared as implementing the protocol UITextFieldDelegate (in addition to UITableViewDataSource, I assume)

then in your makeTextField: (NSString*)text placeholder: (NSString*)placeholder method, you need to have something like:

-(UITextField*) makeTextField: (NSString*)text
              placeholder: (NSString*)placeholder  {
    UITextField *tf = [[UITextField alloc] initWithFrame:CGRectMake(40, 0, 150, 40)];
    tf.placeholder = placeholder;
    // (...)
    tf.delegate = self;
    return tf ;
}

Then you need to setup the delegate methods correctly. In the following example, I have a nav bar, since the numbers pad doesn't have a return or a done button. I setup a button that will act as the done button (you may have another way of making the keyboard go, and switching between text fields will trigger the end of edition anyway):

- (void) textFieldDidBeginEditing:(UITextField *)textField {
    UIBarButtonItem *doneBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(doneEditing:)];

    self.navBar.topItem.rightBarButtonItem = doneBtn;
}

- (void) doneEditing:(id) sender {
    if(phoneFieldTextField.isFirstResponder) {
        [phoneFieldTextField resignFirstResponder];
    }
    // (...)
    self.navBar.topItem.rightBarButtonItem = nil;
}

Then, the magic happens in the textDidEndEditing delegate method:

- (void)textFieldDidEndEditing:(UITextField *)textField {
    if ( textField == phoneFieldTextField ) {
        self.phone = [self formatPhoneNumber:textField.text deleteLastChar:YES] ; // convert
        [phoneFieldTextField setText:self.phone]; // display
    }
    // (...)
}
krug
  • 216
  • 1
  • 2