Objective-C loop to compare two textfields for passwords - ios

I am trying to extend one of my iOS projects to ask the user to input a password and then re-type the password and if the passwords don't match, keep doing it until they do. I can do it by setting up IBOutlets in my project and then do the comparison, but then how do I loop the comparison if they don't match? I've searched a lot for this but haven't found a simple answer other than a github project called PTPasscodeController, which is too complicated for what I want do, i.e., simple input and comparison, not extensive security.
I tried to get this to work by UIAlertController, but it doesn't seem possible to initialize and then check the loop for the comparison of the two textfields. That is, I know how to get an array of textfields through UIAlertController, but not how to keep putting up the alert when the two textfields don't match.
If there is code to accomplish this, or a better way, I would appreciate the help a lot!

how do I loop the comparison if they don't match?
You don't loop at all. In UI environments you accomplish "ask again" behavior by disabling actions and showing errors.
I am assuming that in addition to the "password" and "retype password" fields you have some kind of the "I am done" button (e.g. "Next", "Done", etc.) which should be disabled initially.
You should respond to textField:shouldChangeCharactersInRange:replacementString: in the protocol of text fields, check the two passwords for equality, and do one of two things:
If both passwords are non-empty, valid, and equal, enable the "Next" button
Otherwise, put red border or some other visual feedback on the "retype password" field, to tell end-user that they made a mistake while entering their password the second time.

Here is my code that works.
I have two view controllers. The first view controller is a tableView Controller that checks for a password required in the settings bundle. If a password is required, it performs a segue to the second view controller.
The second view controller compares a password to a re-entered password and sets the text color depending on whether the two match. Also, if a password was previously set, it deletes the second password request from the view (by checking the subview tags from the storyboard) and just compares the previously entered password against the password the user now enters.
Not very elegant, but it works for my purposes.
The first view controller's relevant code:
NSUserDefaults *defaults =[NSUserDefaults standardUserDefaults];
NSLog(#"defaults %#", [defaults
dictionaryRepresentation]);
BOOL passwordProtect = [[defaults valueForKey:#"password"]boolValue];
if(passwordProtect)
{
self.passwordEntered = YES;
[self performSegueWithIdentifier:#"passcodeViewController" sender:self];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"passcodeViewController"]){
NRCPasscodeControllerViewController *destViewController = segue.destinationViewController;
destViewController.password = self.password;
}
}
The second view controller's code:
#interface NRCPasscodeControllerViewController : UIViewController <UITextFieldDelegate>
#property (weak, nonatomic) IBOutlet UITextField *password1;
#property (weak, nonatomic) IBOutlet UITextField *password2;
#property (strong, nonatomic) NSString *password;
#property (nonatomic) BOOL passwordWasEntered;
#end
implementation file:
#import "NRCPasscodeControllerViewController.h"
#interface NRCPasscodeControllerViewController ()
#end
#implementation NRCPasscodeControllerViewController
- (IBAction)checkPasswords:(id)sender {
// if a password has not been entered, display
// both password fields
if((self.passwordWasEntered == NO)){
// password1 must be 4 characters in length
if ([self.password1.text length] == 4) {
self.password1.textColor = [UIColor greenColor];
} else {
self.password1.text =#"Invalid!";
self.password1.textColor = [UIColor redColor];
}
// password2 must be 4 characters in length
if ([self.password2.text length] == 4) {
self.password2.textColor = [UIColor greenColor];
} else {
self.password2.text =#"Invalid!";
self.password2.textColor = [UIColor redColor];
}
// passwords must match
if([self.password1.text isEqual:self.password2.text]){
self.password2.textColor = [UIColor greenColor];
// passwords match, so set the password and segue back
self.password = self.password1.text;
[self performSegueWithIdentifier:#"unwindToTableView" sender:self];
}
else{
self.password2.textColor = [UIColor redColor];
}
}
// if password has already been entered, just check the first password
// field against the entered password.
else if (self.password == self.password1.text){
// password matches entered password, so set the password and segue back
self.password1.textColor = [UIColor greenColor];
[self performSegueWithIdentifier:#"unwindToTableView" sender:self];
}
else{
// password does not match entered password
self.password1.textColor = [UIColor redColor];
self.password = self.password1.text;
}
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
return YES;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.password1.delegate = self;
self.password2.delegate = self;
[self.navigationItem setHidesBackButton:YES animated:YES];
// Do any additional setup after loading the view from its nib.
}
-(void)viewWillAppear:(BOOL)animated{
// if self.password is not nil, a password has already been entered
// so only display one password field to check.
if(self.password){
[[self.view viewWithTag:3] removeFromSuperview];
[[self.view viewWithTag:2] removeFromSuperview];
self.passwordWasEntered = YES;
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#end
and in the first controller that receives the unwind segue:
-(IBAction)unwindFromPasscodeController:(UIStoryboardSegue*)segue{
NSLog(#"Unwound from passcode controller");
NRCPasscodeControllerViewController *sourceViewController = segue.sourceViewController;
self.password = sourceViewController.password;
}

Related

Disable Button until all textfields have been Filled Xcode

I have looked everywhere for this answer but I haven't gotten a simple easy to follow answer to this question. I have 8 text fields that I need to fill out before I click and submit before moving onto the next page. I have hooked up each of the textfields to view controller.h file but I don't know how to disable the submit button easily step by step. Thank you so much for the help in advance.
I have tried this from a previous post but I could not get it to work..
Make an Outlet for every UITextField and create an IBAction in your .h:
IBOutlet UITextField *textField1;
IBOutlet UITextField *textField2;
IBOutlet UITextField *textField3;
IBOutlet UIButton *button
- (IBAction)editingChanged;
Connect all the outlets and connect the IBAction to every textfield with editingChanged:
- (IBAction)editingChanged {
if ([textfield1.text length] != 0 && [textfield2.text length] != 0 && [textfield3.text length] != 0) {
[button setEnabled:YES];
}
else {
[button setEnabled:NO];
}
}
Note that you can also use [textfield.text isEqualToString:#""] and put a ! in front of it (!means 'not' in objective-c) to recognize the empty textField, and say 'if the textField is empty do...'
- (void)viewDidLoad {
[super viewDidLoad];
[button setEnabled:NO];
}
try this,
1) You need to add textfiled delegate function to all your textfiled.
.h
#interface ViewController : UIViewController <UITextFieldDelegate>
.m
- (void)viewDidLoad
{
[super viewDidLoad];
//set submit button disable
submitbtn.enable=NO
textfiled1.delegate = self;
textfiled2.delegate = self;
textfiled3.delegate = self;
textfiled4.delegate = self;
textfiled5.delegate = self;
textfiled6.delegate = self;
textfiled7.delegate = self;
textfiled8.delegate = self;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
//check your all texfield length if not equal to zero in below if(condition)
if(alltextfiled_length != 0)
{
submitbtn.enable=YES
}
else
{
submitbtn.enable=NO
}
}
I suggest you read the documentation on connecting outlets.
To handle changes in the text fields your view controller class could conform to the UITextFieldDelegate protocol, assign your view controller as the delegate of each text field, and implement - (BOOL)textFieldShouldReturn:(UITextField *)textField which will be called when tapping the Return button on each text field's keyboard.

Setting UILabel text is not working

Here is my .h file
#import <UIKit/UIKit.h>
#interface PersonViewController : UIViewController
#property(strong,nonatomic) NSString *personTitle;
And here is my .m file
#interface PersonViewController ()
#property (weak, nonatomic) IBOutlet UILabel *titleView;
#end
#implementation PersonViewController
//stuff …
-(void)setPersonTitle:(NSString *)personTitle
{
[self.titleView setText:personTitle];// also self.titleView.text=personTitle
[self.titleView setNeedsDisplay];
NSLog(#"The title shoud match as %# :: %#",personTitle,self.titleView.text);
}
-(NSString *)personTitle
{
return self.titleView.text;
}
//… more stuff
#end
The logging shows that the value is (null) for self.titleView.text whereas personTitle prints the appropriate value.
I remember doing this same thing a number of times and it worked. Any ideas why it’s failing this time?
update I use storyboard to set my scenes. And I am using xcode-5 and iOS-7
update: how I call
The user clicks a button, leading to a push segue
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSLog(#"enter prepare for segue.");
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
if ([segue.identifier isEqualToString:the_identifier_for_person]) {
NSLog(#"segue to person is progressing“);
if ([segue.destinationViewController isKindOfClass:[PersonViewController class]]) {
NSLog(#"segue to person destination is a match");
PersonViewController *aPerson = (PersonViewController *)segue.destinationViewController;
aPerson.personTitle=((MyItem*)self.allItems[indexPath.row]).title;
NSLog(#"segue to person is done");
}
}
}
This sounds like you forgot to wire up your UILabel in the storyboard. Can you confirm that self.titleView is not null?
View controllers create their views on demand, but can spot that only via a call to view. When the view is loaded, your outlets will be populated.
Either call view to force loading or keep the string in abeyance until you get viewDidLoad.
(aside: prior to iOS 6, views would also be released in low-memory situations so the idiomatic thing is to store the string and populate on viewDidLoad)
Having accepted another answer, I wanted to show the pattern that I actually used to solve the problem, in case someone else comes looking. This pattern is best practice (yes, I forgot it for a long moment there).
#pragma mark - update UI
-(void)setPersonTitle:(NSString *)personTitle
{
_personTitle=personTitle;
if (self.view.window) [self updateUI];//only if I am on screen; or defer to viewWillAppear
}
-(void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self updateUI];
}
-(void)updateUI
{
self.titleView.text=self.personTitle;
}
It is always important to update the ui when the data has changed, which is why I must make the call inside setPersonTitle. But because the IBOutlets are not yet set when I set personTitle in prepareForSegue, then I must also make the call inside viewWillAppear.
Do you actually call the -(void)setPersonTitle:(NSString *)personTitle method?
It seems that you aren't calling it correctly which would result in the title being null.
After reviewing the prepareForSeque it is clear that you are not calling the method. You are actually just changing the #property named personTitle.
In the viewDidLoad you should have it so that self.titleView.text = self.personTitle;

Checking UITextField.text from the delegate?

In my application, I have 3 UITextField declared in my header:
#property (strong, nonatomic) IBOutlet UITextField *email;
#property (strong, nonatomic) IBOutlet UITextField *username;
#property (strong, nonatomic) IBOutlet UITextField *password;
Once I have the values, I assign them to my variables and continue on with other account creation processes.
What I am trying to do is the following, as of right now, my UITextField "Tab" from one another using the .tag property declared in the delegate method as follows:
-(BOOL)textFieldShouldReturn:(UITextField*)textField;
{
NSInteger nextTag = textField.tag + 1;
UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];
if (nextResponder) {
[nextResponder becomeFirstResponder];
} else {
[textField resignFirstResponder];
}
return NO;
}
I have an additional alertView that contains a label that alerts the users for things such as:
-> Username already exists, Email already exists, Valid password must contain at least 5 characters, etc..
Instead of alerting the users that they have made any of the mistakes mentioned above when the click on the "sign up" button, i'd like of them to know of their mistakes as soon as the textfield resigns firstResponder and has at least 1 character in it's .text property.
Can someone please point me in the right direction, thanks!
UPDATE
I have attempted to implement the textFieldDidEndEditing delegate method:
-(BOOL)textFieldDidEndEditing:(UITextField*)textField {
if (self.passwordTF.text.length <=4)
{
NSString *noValidPW = #"Passwords must be atleast 5 characters";
NSMutableAttributedString *noValidPWAttrString = [[NSMutableAttributedString alloc] initWithString:noValidPW attributes:alertAttrs];
self.alertLabel.attributedText = noValidPWAttrString;
[UIView transitionWithView:[self redAlert]
duration:0.2
options:UIViewAnimationOptionTransitionCrossDissolve
animations:NULL
completion:NULL];
self.redAlert.hidden = NO;
}
return NO;
}
But that is not working, that is giving me the password alert as soon as I stop editing ANY field, not just the password field.
Update
This is the code that generates animates my alert :
NSString *noValidPW = #"Passwords must be atleast 5 characters";
NSMutableAttributedString *noValidPWAttrString = [[NSMutableAttributedString alloc] initWithString:noValidPW attributes:alertAttrs];
self.alertLabel.attributedText = noValidPWAttrString;
[UIView transitionWithView:[self redAlert]
duration:0.2
options:UIViewAnimationOptionTransitionCrossDissolve
animations:NULL
completion:NULL];
self.redAlert.hidden = NO;
I have similar alerts for other error types (for other textfields), so its important to be able to check which textfield was the one that just ended being edited, does it have any characters on its .text property? If so, it needs to pass the validation code I provided right above this.
You can use -textFieldDidEndEditing: for that. Your missing piece was making sure that the textField that called the delegate method is the one you are interested in.
E.g.:
- (void)textFieldDidEndEditing:(UITextField *)textField
{
// Check only if the user didn't leave the textfield blank
if ([textField.text length] >= 1)
{
if (textField == self.password)
{
NSString *passwordText = textField.text;
if ([passwordText length] < 5)
{
[self showPasswordLengthError];
}
else if ( /* check other password stuff */ )
{
[self showPasswordOtherError];
}
}
else if (textField == self.username)
{
// Check username conditions and show errors accordingly
}
else if (textField == self.email)
{
// Check email stuff
}
// etc.
}
}
- (void)showPasswordLengthError
{
NSString *noValidPW = #"Passwords must be at least 5 characters";
NSMutableAttributedString *noValidPWAttrString = [[NSMutableAttributedString alloc] initWithString:noValidPW attributes:alertAttrs];
self.alertLabel.attributedText = noValidPWAttrString;
[UIView transitionWithView:[self redAlert]
duration:0.2
options:UIViewAnimationOptionTransitionCrossDissolve
animations:NULL
completion:NULL];
self.redAlert.hidden = NO;
// Hide alertLabel after 5 sec
[self performSelector:#selector(hideAlertLabel) withObject:nil afterDelay:5.0];
}
- (void)hideAlertLabel
{
self.redAlert.hidden = YES;
}

ios login screen issue

I've been developing an iOS app that includes a login screen. This screen has two UITextField where I input the username and the password.
Now, the issue recalls in the following: I've set up an "else if" to trigger an UIAlertView if the field are in blank.
The UIAlertView DOES pop up but... in the next View Controller.
Another issue is... It check if the username and the password are right or not and it jumps into the next View Controller as well.
This is odd because I set an "if" condition to check that the text in both UITextFields must match in order to trigger the next View Controller.
I've got the hunch that another method linked to the login action might be interfering in the process.
I will post the segment of the code concerning the login:
- (void)btn_submit:(id)sender{
NSString *user = self.usuari.text;
NSString *pass = self.contrasenya.text;
NSString * urlBase = #"http://www.v2msoft.com/clientes/lasalle/curs-ios/login.php?username=lluis&password=seguro";
[JSONHTTPClient getJSONFromURLWithString:urlBase completion:^(id json,JSONModelError *err){
JsonUser * user = [[JsonUser alloc]initWithDictionary:json error:nil];
if(user.succeed){
self.user_id = user.user_id;
[self performSegueWithIdentifier:#"Login" sender:self];
}
else{
NSLog(#"error");
}
}];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if (([self.usuari.text isEqualToString:#"lluis"]) && ([self.contrasenya.text isEqualToString:#"seguro"])){
Supermarket * supermercat = segue.destinationViewController;
supermercat.user_id = self.user_id;
}
else if (_usuari.text.length == 0 || _contrasenya.text.length == 0)
{
[[[UIAlertView alloc] initWithTitle:#"Alerta!"
message:#"Camps buits!"
delegate: nil
cancelButtonTitle:#"Cancelar"
otherButtonTitles:nil
] show];
}
}
And, also, the few lines of the "JsonUser" class:
#import "JSONModel.h"
#interface JsonUser : JSONModel
#property BOOL succeed;
#property int user_id;
I think that I might be making a mistake by sending the parameters to the server through the URL and that might override the login.
I'd appreciate any help or advice regarding this matter.
It is already too late for your check within prepareForSegue. If you had the chance to stop the presentation of the segue there the method would be called shouldPresentSegue or something like that. Perform your check (the else if) before you call performSegue.
whether u connect segue from submit button to next view controller or segue from login view to nextviewcontroller. Make Sure your segue only from login screen to nextviewcontroller not from submit button.

UITextField clear when selected, but also restore data if nothing is input

I have a table with UITextFields, and I want to make it so that when a textfield is selected, the current info is cleared (to allow new input), but if the user decides (after selecting) that they don't want to change it, I want them to be able to click elsewhere and the data from before reappears in the textfield.
Is there an easy way to do this?
A good way to do this that's nice and user friendly is to use the placeholder property. Set up your viewController as the textfield's delegate (as described by Andeh) then add the following code:
- (void)textFieldDidBeginEditing:(UITextField *)textField {
textField.placeholder = textField.text;
textField.text = #"";
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
if (textField.text.length == 0) {
textField.text = textField.placeholder;
}
textField.placeholder = #"";
}
And you're done. If you have multiple UITextFields on the page and you don't want this behaviour for all of them you can add a check to the methods above.
In the textFieldDidBeginEditing delegate method, save the current value to a persisting variable before clearing the UITextField. Then in the didFinishEditing delegate method, if the new length of the user input is 0 set the text back to the stored value!
UITextField Delegate docs for reference.
First I think you have two sets of behaviors here.
The text field must clear the value when you begin editing. This exists: -clearsOnBeginEditing.
The text field must restore the previous text if text is empty. Subclassing seems the better solution.
Here is a possible sample class:
// MyRestoringTextField.h
#interface MyRestoringTextField : UITextField
#end
// MyTextField.m
#interface MyRestoringTextField ()
#property (strong, nonatomic) NSString *previousText;
#end
#implementation MyRestoringTextField
- (BOOL)becomeFirstResponder
{
BOOL result = [super becomeFirstResponder];
self.previousText = self.text;
return result;
}
- (BOOL)resignFirstResponder
{
BOOL result = [super resignFirstResponder];
if (self.text.length == 0)
self.text = self.previousText;
return result;
}
#end
Hope that helps.
To clear and then restore a textField if you fail to make an entry, use the following delegates as such:
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
[[NSUserDefaults standardUserDefaults] setObject:textField.text forKey:kTextFieldIdentifier];
textField.text = #"";
}
-(BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
if ([textField.text isEqualToString:#""])
textField.text = [[NSUserDefaults standardUserDefaults]
stringForKey:kTextFieldIdentifier];
return YES;
}
As of iOS 8.1, textFieldDidBeginEditing is already receiving a cleared text. You should use
-(BOOL) textFieldShouldBeginEditing:(UITextField *)textField
to initialized the placeholder field.

Resources