views:

709

answers:

5

I would like to create a pin-code dialogue, like the one you can switch on on the iPhone.

For those who have not seen it, it consists of four boxes and a number keypad. When you enter a digit, a dot appears in the first box. And so forth. When you hit the delete button, the last dot is removed.

I have this set up as four UITextFields and in my delegate I listen to:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
  [self performSelector:@selector(pickNext:) withObject:textField afterDelay:0.0];
  return YES;
}

The pickNext: method will switch to the next UITextField, like this:

- (void)pickNext:(UITextField*)textField
{
  switch ([textField tag]) {
    case 1:
      [pin2 becomeFirstResponder];
      break;
    case 2:
      [pin3 becomeFirstResponder];
      break;
    case 3:
      [pin4 becomeFirstResponder];
      break;
    case 4:
      [textField resignFirstResponder];
      break;
    default:
      break;
  }
}

This actually works, but the problem for me is that the delete key does not produce any notification when the UITextField is already empty. So I have no way of moving to the previous UITextField.

So does anyone have a better sugestion of how to solve this problem. I'm thinking hidden textfield...??

A: 

What you need to do is hook a method up of each of the four UITextField's "ValueChanged" event and in that method check to see if the sender's text length is 0.

You can hook ValueChanged event for the four UITextfields up to the same method and switch on tag as you are doing above. The following code will do the trick.

-(IBAction) pinChanged: (id)sender {

    UITextField *currentField = (UITextField*) sender;

    // if the field thqt has just been changed is blank
    if ([currentField.text length] == 0) {

     // switch on the fields tag, and go to the previous field
     switch (currentField.tag) {
      case 1:
       // in first field already, stay here!
       break;
      case 2:
       // go back to previous field
       [pin1 becomeFirstResponder];
       break;
      case 3:
       // go back to previous field
       [pin2 becomeFirstResponder];
       break;
      case 4:
       // go back to previous field
       [pin3 becomeFirstResponder];
       break;
      default:
       break;
     }
    }
}
Damian
Well, ValueChanged apparently does nothing for a UITextField, at least I get no events. But "Editing changed" does send events, but again, not when the field is empty and I hit the delete key.
Kobski
+1  A: 

OK, so I solved it myself. The hidden textfield was the way to go. Even though it is hidden, you can still make it the first responder and the keyboard will pop up.

So to summarize:

In viewDidLoad:

[hidden becomeFirstResponder];

And then I listen for the "Editing Changed" event and update the four visible UITextField with one character each. Like this:

- (IBAction)textChanged:(UITextField*)hiddenField
{
  NSString *hiddenText = hiddenField.text;

  [self setOneTextField:pin1 toString:hiddenText atIndex:0];
  [self setOneTextField:pin2 toString:hiddenText atIndex:1];
  [self setOneTextField:pin3 toString:hiddenText atIndex:2];
  [self setOneTextField:pin4 toString:hiddenText atIndex:3];
}

- (void)setOneTextField:(UITextField*)textField toString:(NSString*)string atIndex:(NSInteger)index
{
  if ([string length] > index)
    textField.text = [string substringWithRange:NSMakeRange(index, 1)];
  else
    textField.text = @""; 
}

To restrict the number of characters in the hidden UITextField to four characters I implement the delegate method "shouldChangeCharactersInRange":

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
  bool okToEdit = YES;

  if (range.location > 3)
  {
    okToEdit = NO;
  } else if (range.location == 3) {
    [self performSelector:@selector(sendPinCodeNotification) withObject:nil afterDelay:0.0];
  }
  return okToEdit;
}

- (void)sendPinCodeNotification
{
  [[NSNotificationCenter defaultCenter] postNotificationName:PINCODE_NOTIFICATION object:[NSString stringWithFormat:@"%@%@%@%@", pin1.text, pin2.text, pin3.text, pin4.text]];
}

And as you can see I send a notification when the fourth digit has been entered.

Kobski
A: 

I am new to coding and would like to set this up in my app. How or where do you start this process... In you AppDelegate file? Please provide me a jump start. I'm invoking this process from the UIButtonTypeInfoDark on the NavgationBar/TabBar Controller from the MainWindow

SympleMyne
A: 

I need help implementing this code. I am receiving 3 errors and 10 Warnings trying to make this work. Please help.

import "KeyPadViewController.h"

@implementation KeyPadViewController @synthesize textField, hiddenField; @synthesize pin1, pin2, pin3, pin4;

  • (void)viewDidLoad { [hidden becomeFirstResponder]; }

  • (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { } return self; }

  • (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { [self performSelector:@selector(pickNext:) withObject:textField afterDelay:0.0]; return YES; }

  • (void)pickNext:(UITextField*)textField { switch ([textField tag]) { case 1: [pin2 becomeFirstResponder]; break; case 2: [pin3 becomeFirstResponder]; break; case 3: [pin4 becomeFirstResponder]; break; case 4: [textField resignFirstResponder]; break; default: break; } }

  • (void)dealloc { [super dealloc]; }

  • (void)viewDidUnload { }

  • (IBAction)dismissAction:(id)sender { [self.parentViewController dismissModalViewControllerAnimated:YES]; }

  • (IBAction)textChanged:(UITextField*)hiddenField { NSString *hiddenText = hiddenField.text;

    [self setOneTextField:pin1 toString:hiddenText atIndex:0]; [self setOneTextField:pin2 toString:hiddenText atIndex:1]; [self setOneTextField:pin3 toString:hiddenText atIndex:2]; [self setOneTextField:pin4 toString:hiddenText atIndex:3]; }

  • (void)setOneTextField:(UITextField*)textField toString:(NSString*)string atIndex:(NSInteger)index { if ([string length] > index) textField.text = [string substringWithRange:NSMakeRange(index, 1)]; else textField.text = @""; }

  • (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { bool okToEdit = YES;

    if (range.location > 3) { okToEdit = NO; } else if (range.location == 3) { [self performSelector:@selector(sendPinCodeNotification) withObject:nil afterDelay:0.0]; } return okToEdit; }

  • (void)sendPinCodeNotification { [[NSNotificationCenter defaultCenter] postNotificationName:PINCODE_NOTIFICATION object:[NSString stringWithFormat:@"%@%@%@%@", pin1.text, pin2.text, pin3.text, pin4.text]]; }

@end

SympleMyne
+1  A: 

Check out this: CPLockController

christo16