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.
Related
I am using SWRevealViewController to implement a side menue in my application. in storyboard i have set the sw_front segue. this segue is loading the MainView on every start of application. now i want to give the user the possibility to change the first displayed viewcontroller.
How can i do that? I know there is a property setFrontViewController: but where should i use this to load the front during application start?
I cant use it in the viewDidLoad of the frontViewController which i set in Storyboard. That could not be a solution, so i would need to do it in every view controller to grab and check the "should load another view controller as first or frontViewController"
I have understood what you have problem and what you want to do i have also the same kind of thing to do, so what i have done create the custom segue in the swrevealviewcontroller that i have also mention in my code.
and modified the two methods perform and loadstoryboardcontroller methods, you can also see in my code.
It will definetely work for you as well.
Have a happy coding cheers.
My code is below:
I have done this the code you need to change is below.
- (void)loadStoryboardControllers
{
if ( self.storyboard && _rearViewController == nil )
{
//Try each segue separately so it doesn't break prematurely if either Rear or Right views are not used.
#try
{
[self performSegueWithIdentifier:SWSegueRearIdentifier sender:nil];
}
#catch(NSException *exception) {}
#try
{
[self performSegueWithIdentifier:SWSegueFrontIdentifier sender:nil];
}
#catch(NSException *exception) {}
#try
{
[self performSegueWithIdentifier:SWSegueRightIdentifier sender:nil];
}
#catch(NSException *exception) {}
#try
{
[self performSegueWithIdentifier:SWSegueCustomIdentifier sender:nil];
}
#catch(NSException *exception) {}
}
}
#pragma mark - SWRevealViewControllerSegueSetController segue identifiers
NSString * const SWSegueRearIdentifier = #"sw_rear";
NSString * const SWSegueFrontIdentifier = #"sw_front";
NSString * const SWSegueRightIdentifier = #"sw_right";
NSString * const SWSegueCustomIdentifier = #"sw_custom";
#pragma mark - SWRevealViewControllerSegueSetController class
#implementation SWRevealViewControllerSegueSetController
- (void)perform
{
SWRevealControllerOperation operation = SWRevealControllerOperationNone;
NSString *identifier = self.identifier;
SWRevealViewController *rvc = self.sourceViewController;
UIViewController *dvc = self.destinationViewController;
if ( [identifier isEqualToString:SWSegueFrontIdentifier] )
operation = SWRevealControllerOperationReplaceFrontController;
else if ( [identifier isEqualToString:SWSegueRearIdentifier] )
operation = SWRevealControllerOperationReplaceRearController;
else if ( [identifier isEqualToString:SWSegueRightIdentifier] )
operation = SWRevealControllerOperationReplaceRightController;
else if ( [identifier isEqualToString:SWSegueCustomIdentifier])//your conditional segue
operation = SWRevealControllerOperationReplaceFrontController;
if ( operation != SWRevealControllerOperationNone )
[rvc _performTransitionOperation:operation withViewController:dvc animated:NO];
}
#jogshardik has the correct answer for my question but i found also another solution.
In your storyboard you are connecting a custom segue "sw_front" with your viewcontroller. This viewcontroller is on every initial start your first front viewcontroller. So you just have to check in this controller if you need to set another front viewcontroller.
i set up a notification in this "sw_front" viewcontroller and also in your Side Menue View Controller add the notification handler.
if you want to change the view just fire the notification and let do the notifaction handler the rest (set your setFrontViewController: property).
So this also works and is update stable if you need to keep attention on newer versions.
if this information is not enough for you, give me a hint and i will post an example code or project when i have time.
I am using this code from Parse:
[PFUser logInWithUsernameInBackground:namerL password:passerL
block:^(PFUser *user, NSError *error) {
if (user) {
[self performSegueWithIdentifier:#"signinToInbox" sender:self];
} else {
NSLog(#"%#",error);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Login Failed." message:#"Invalid Username and/or Password." delegate:self cancelButtonTitle:#"Okay" otherButtonTitles:nil];
[alert show];
}
}];
Whenever I click on the sign in button without typing in anything, it doesn't log any errors. I need it to count it as an error if there is no such user and or if the password is correct. But even if the account details are not correct, it is still going to the segue.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { I believe that it's calling the segue because it is still in didSelectRowAtIndexPath. I just don't know how to stop it from calling the segue that's connected it (by default).
Maybe this problem isn't about parse login at all. From the comments, it seems to be about segues attached to table view cells.
If you want to perform a segue conditionally, it shouldn't be attached directly to the table view cell. Instead draw the segue between view controllers in IB (ctrl-drag from the view controller's status bar to the destination vc). Give that segue a name, like "goodLogin", then perform it manually upon a condition.
So if you only want to segue when a login succeeds ...
// parse login
if (!error) {
[self performSegueWithIdentifier:#"goodLogin"];
} else {
// don't perform a segue. Stay here and change ui to indicate an error
}
This is the code I use:
[PFUser logInWithUsernameInBackground:username password:password block:^(PFUser *user, NSError *error) {
if(!error){
[self loggedInSuccessfully];
} else {
[self loginFailedWithError:error];
}
}];
When doing any API call, I would always check the error variable because if it exists then you know the method failed.
EDIT
From our discussion it appears that you are having problems with your UITableView performing the segue when you click on a cell.
It is possible to disable a segue by returning NO from the shouldPerformSegueWithIdentifier method
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
{
if([identifier isEqualToString:#"signinToInbox"]){
return YES;
}
return NO;
}
This will disable all segues on the UIViewController apart from your signinToInbox
However, I believe you may have your UIStoyboard set up incorrectly, there shouldn't be the default segue enabled. You should only need to perform the segue in the code after you have called the login function. Therefore in the storyboard you should be using the custom segue option.
If you have a segue set up already on the UIStoryboard you should be able to click on it and in the Attributes inspector panel change the drop down from model to custom
It seems you have problems with your segues and table views. Try the following in Xcode 6.
First delete your current segue.
Ctrl + drag as shown:
After that, choose the action segue you want (push, modal or custom). Then make sure the identifier matches the one in the code.
Now simply make the following changes in the code
[PFUser logInWithUsernameInBackground:namerL password:passerL block:^(PFUser *user, NSError *error) {
if (!error) {
[self performSegueWithIdentifier:#"signinToInbox" sender:self];
} else {
NSLog(#"Error logging in: %#",error);
[[[UIAlertView alloc] initWithTitle:#"Login Failed" message:#"In valid username and/or password" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil] show];
}
}];
I'm new to Objective-C and have a question. Did the search multiple times but I couldn't find what I was looking for.
I'm using storyboard for this app. On the homescreen you've got some buttons with labels above them. Those labels should tell a number. When pushing the button you go to a new viewController where you have input that (after 'save') goes back to the homescreen and updates the label with the correct number. All that works great for one button and I'm very happy about it.
The problems are:
1. Since I have multiple buttons with labels, I want to use the same viewController to give input over and over again. I tried connecting every button to slide to the viewController under the identifier "AddData", but Xcode doesn't allow the same identifiers twice or more in storyboard. So I would need something else for this. Any idea?
2. Currently I use the following code to bring back the data to the homescreen:
homeScreenViewController
- (IBAction)unwindToHomeScreen:(UIStoryboardSegue *)segue;
{
inputDataViewController *source = [segue sourceViewController];
self.logoOneLabel.text = source.endTotalNumber;
}
inputDataViewController:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if (sender != self.saveButton) {
return;
} else {
if (endTotalLabelNumber > 0) {
self.endTotalNumber = [NSString stringWithFormat:#"%.0f", totalLabelNumber + endTotalLabelNumber];
} else if (endTotalLabelNumber == 0 && totalLabelNumber == 0){
self.endTotalNumber = 0;
} else {
self.endTotalNumber = [NSString stringWithFormat:#"%.0f", totalLabelNumber + endTotalLabelNumber];
}
}
}
This works great for the one button, but how to use this with multiple? I heard about Delegates to use the same viewController multiple time and get data back to different places, but I just don't get it. Any help?
You shouldn't need delegates.
What you will need is a property on the view controller that handles input to it knows which button it is handling input for.
When you segue to the input controller, set this property, based on which button was pushed. When you unwind back, fetch this property to know which label to modify.
For example, in your input view controller's .h file, add a property like this:
#property (nonatomic,assign) NSInteger handlingTag;
Or something, whatever name makes sense to you.
Now you need to implement your home screen view controller's prepareForSegue:sender:.
Use the sender argument to determine which button was pushed, and based on that, set the input view controller's new handlingTag property based on the button in a way that you will know what to do with it when we unwind.
Now in the unwind method:
switch (source.handlingTag)
Create a switch structure based on the source's handlingTag property, and set the appropriate label based on this value.
As Jeff points out in the comments, it'd be a really good idea to define an NS_ENUM to use here for the property rather than an NSInteger. The NS_ENUM would allow you to name the values you're using.
There is a few different way to implement what you need. But i think most common its a delegate.
This is how your inputDataViewController looks like:
#import <UIKit/UIKit.h>
#protocol inputDataDelegate;
#interface inputDataViewController : UIViewController
#property (weak) id<inputDataDelegate> delegate;
#property (strong, nonatomic) NSNumber *buttonTag;
#end
#protocol inputDataDelegate <NSObject>
-(void) inputDataViewControllerDismissed:(id)data;
#end
Then in #implementation, you should in "save" button action, message to you delegate method :
[self inputDataViewControllerDismissed:#{#"buttonTag":buttonTag,#"endTotalNumber":endTotalNumber}
Next in homeScreenViewController connect delegate :
#interface homeScreenViewController : UIViewController<inputDataDelegate>
After that in #implementation:
-(void)inputDataViewControllerDismissed:(id)data
{
// if you use modal
[self dismissViewControllerAnimated:YES completion:nil];
// or if you use push
//[self.navigationController popViewControllerAnimated:YES];
switch (data[#"buttonTag"]) {
case 1:
self.lableWtiTagOne = data[#"endTotalNumber"];
break;
case 2:
self.lableWtiTagTwo = data[#"endTotalNumber"];
break;
// number of cases depend how many buttons you have
}
Also, most important, thing didn't forget send self to our delegate:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"inputDataController"])
{
inputDataViewController *inputCtrl = [segue destinationViewController];
inputCtrl.delegate = self;
inputCtrl.buttonTag = sender.tag
}
}
can't for the life of me figure this out.
so I'm creating a contact list app, very similar to the one that comes with iOS7.
a user creates a name with the GUI on a detail view controller.
user goes back to the main contact list page (master view controller)
user taps on the name/contact they just created
brings them back to the view controller, but it shows what they had entered into the text fields.
User starts with this screen:
User hits the add contact button, adds in the user data here which brings them back to the first screen:
Next, they are brought to the first screen and are able to tap on the contact they've added and should bring up all the details entered (firstname,lastname,phone number etc):
here's my code in my master view controller:
- (void) detailViewControllerWillDissapear: (DetailViewController *) dvc {
NSLog(#"test");
DirectoryEntry *person = [[DirectoryEntry alloc] init];
person.firstName = dvc.txtFirstName.text;
person.lastName = dvc.txtLastName.text;
person.phoneNumber = dvc.txtPhoneNumber.text;
person.address = dvc.txtAddress.text;
[self.entries addObject:person];
NSLog(#"index is %#", person.firstName);
NSLog(#"count of array %lu", (unsigned long)self.entries.count);
dvc.holdData = person;
[self.tableView reloadData];
}
here's my segue:
#pragma mark - segue
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
//DirectoryEntry *person = [[DirectoryEntry alloc] init];
DetailViewController *dvc = segue.destinationViewController;
dvc.delegate = self;
if ([segue.identifier isEqualToString:(#"tappedData")]) {
NSLog(#"what is the person class have %#", dvc.holdData);
// NSLog(#"dvc.holdData = %#", person.firstName);
}
}
so I've created the public variable holdData which is meant to hold all the data which was entered from the array into the Person class. but whenever I add it, just comes up with a Null value. I am able to access the variables i.e. holdData.firstName, but just everything is null.
any ideas? really confused, thanks.
The dvc that you have in prepareForSegue is almost certainly not the same object that you had in the detailViewControllerWillDissapear method. (Try printing their addresses to prove that for yourself.) That means that either your main controller should save the Person data and provide it during the segue or you could create a separate data model that each DetailViewController would use directly.
I am using storyboard. It has a login VC , which on success pushes to another VC. When the user enters the credentials and clicked on login button then it hits the server. I have implemented all the logic that contains hitting with server in a separate class which has delegate. When we get response then the control goes to the delegate method implemented in login VC. In the delegate method if status is success then only the login VC must be pushed in to another VC.
In Login VC
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
{
Util *util = [[Util alloc]init];
util.delegate = self;
NSMutableDictionary *request = [[NSMutableDictionary alloc]init];
[request setValue:uname.text forKey:#“username”];
[request setValue:pwd.text forKey:#“password”];
[util body:request];
}
when server returns response it comes to the delegate method implemented in Login VC
- (void)response:(NSDictionary *)response
{
//here i am going to check the status if it is success i will go to new VC else in same VC
}
here i am unable to go to another VC since i am not in the shouldPerformSegueWithIdentifier: method.
Why don't you call
[self performSegueWithIdentifier:#"customerView" sender:self];
It looks like you're probably triggering your segue when the login button is tapped. You can't do this if you need to wait for a response from your web service. Instead, try connecting your button to a method when sends off your login request. When you receive your response, you can check if the user's login is valid, and then perform your segue programmatically:
- (void)response:(NSDictionary *)response
{
if (something) // check if response is valid
{
[self performSegueWithIdentifier:#"YourSegueIdentifier" sender:self];
}
else
{
// show error
}
}
This way, you won't need to implement shouldPerformSegueWithIdentifier:sender: at all.