I'm working on an iOS application that involves users filling out text fields and navigating through views. Currently, the navigation is handled sometimes using the navigation bar, and sometimes through buttons. I tried to make some of the fields required: preventing progress through the application if those fields are left blank. This worked for the button based navigation but not for the Navigation Bar. My code is as follows:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"birthPopBus"]) {
currentPopoverSegue = (UIStoryboardPopoverSegue *)segue;
pvc = [segue destinationViewController];
[pvc setDelegate:self];
}
else if([[segue identifier] isEqualToString:#"SecondBusPageSegue"]) {
//This code breaks the next page
//Check fields, and create error message (code removed for readability)...
//If the fields are not filled in, display the alert with generated string.
if(!(first && last && email && phone && address && zip && ssn && birthFilled)){
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Required Fields Missing:"
message:alertMessageMutable
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[message show];
}
//Perform the segue, should only run if all required fields are filled.
else{
[self fillBus1Dictionary];
NSLog(#"application dictionary: %#", self.application);
SecBusPage * secondBusPage = segue.destinationViewController;
secondBusPage.application = self.application;
}
}
}
The error message will show up, but only AFTER the view has changed. So the user gets to SecondBusPage, and gets an alert saying "You didn't fill out fields X, Y, Z." which is very confusing for them. Any ideas for preventing the navigation bar from switching views?
Thanks in advance!
You can easily achieve this by implementing conditional segues like the one in my answer in the below linked post
Conditional Segue Using Storyboard
You cannot cancel a segue from prepareForSegue. Instead of that perform segues based on condtions
You are doing the segue from the storyboard and you cannot cancel a storyboard segue. What you should do is delete that segue and use:
if(!(first && last && email && phone && address && zip && ssn && birthFilled)){
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Required Fields Missing:"
message:alertMessageMutable
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[message show];
}else
{
[self.navigationController pushViewController:yourViewController animation:YES];
}
Storyboards limit flexibility in customizing behaviour.
What I did to take control of the navigation in my app was:
Create an id instance object (property) with name mainDelegate in the AppDelegate of the project, and every time a viewController is presented, I set it to the current view controller using the following in the viewDidAppear method:
appDelegate.mainDelegate = self
I used a custom navigationBar for the navigationController.
In this custom navBar's class, on user's tap on a button, i check:
if ([appDelegate.mainDelegate isKindOfClass:[myViewController class]]){
if ([appDelegate.mainDelegate allFieldsAreFilled]){
//perform action of pushing view
}
}
Here allFieldsAreFilled is a delegate method of myViewController.
Related
I am just getting into Xcode programming, and I need code so that when I click a button, it is a confirmation to take me into another ViewController.
I have an X button on the page, and when clicked I need a UIAlertview to pop up saying 'cancel' and 'next', and when next is pressed, i want it to change to another ViewController. These cancel and next buttons need to be side by side.
First thing you need to do is make sure the view with a button is a delegate of UIAlertView.
#interface ViewController() <UIAlertViewDelegate>
Then you need to add a touch up inside action to the button in your view. When the button is pressed, it creates the alertView and shows it. Note, its delegate is self.
- (IBAction)clickButton:(id)sender {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle: #"Test" message: #"Message" delegate: self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Next", nil];
[alert show];
}
Lastly, you create the delegate function. This is triggered whenever a button is clicked in the alertView. buttonIndex corresponds to the button that was clicked, so we want to present the view when the 1st button is pressed (0 corresponds to cancel, 1 corresponds to next). Realistically, you'd probably want to present a custom view controller that you define.
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if(buttonIndex == 1){
UIViewController* newView = [[UIViewController alloc] init];
[self presentViewController:newView animated:YES completion:nil];
}
}
I need to verify credentials. So the segue is linked from first view controller to second view controller in a sign in button.
When I do the segue without any condition check. the navigation works fine.
But when i control the segue using the code below in first view controller, navigation flow disrupts. When the criterias are met it goes to second view controller but when i try to go back to first view controller through navigation controller the screen turns black.
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
{
if ([userTxt.text isEqualToString:username]&&[passwordTxt.text isEqualToString:password]) {
[self performSegueWithIdentifier:#"signin" sender:nil];
return YES;
}
else
{
UIAlertView *alert=[[UIAlertView alloc]initWithTitle:#"INCORRECT" message:#" INCORRECT USER NAME OR PASSWORD" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil , nil];
[alert show];
return NO;
}
}
Try removing the
[self performSegueWithIdentifier:#"signin" sender:nil];
I'm an iOS newbie trying to get this right...
I'm trying to use shouldPerformSegueWithIdentifier in order to add some validation before allowing the segue to go through.
I'm using an Exit segue through a Bar Button, whose method (unwindToChecklist) is written in the destination view controller (called ChecklistViewController). I'm assuming that's why it isn't working, because the source view controller (AddItemViewController) has no segue called unwindToList, even though it's listed under AddItemViewController's list of elements on the left sidebar in the Storyboard.
I've already ensured that the segue's identifier is named unwindToChecklist in the attributes inspector.
My code:
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
NSLog(#"Checking...");
if ([identifier isEqualToString:#"unwindToChecklist"]) {
if (![self.txtItemTitle.text isEqualToString: #""]) {
return NO;
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Item Name Empty"
message:#"Please fill in the title name text field."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
} else {
return YES;
}
}
return YES;
}
Screenshots of 1) the scene sidebar & 2) attributes inspector:
1)
2)
Thanks in advance!
I have a slide out menu in my app, which has a button that will display a UIActionSheet. This slide out menu just partly covers whatever view controller you previously were on.
- (void)cameraButtonPushed:(UIButton *)sender
{
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:#"Create String" delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:nil otherButtonTitles:#"Take Photo", #"Choose from Library", nil];
[actionSheet showInView:[UIApplication sharedApplication].keyWindow];
//[actionSheet showInView:self.parentViewController.view];
}
As you can see I have tried two ways of showing the action sheet and they both appear to work the same.
The problem that I'm having has to do with the action sheet buttons actually working. It shows up in the parent view controller (view controller that you were on), but if I press any of the buttons they do not work.
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0) {
EditVC *newEditVC = [self.storyboard instantiateViewControllerWithIdentifier:#"editVC"];
[editVC setHidesBottomBarWhenPushed:YES];
[self.parentViewController.navigationController pushViewController:newEditVC animated:YES];
//[self.navigationController pushViewController:newEditVC animated:YES];
} else if (buttonIndex == 1) {
EditVC *newEditVC = [self.storyboard instantiateViewControllerWithIdentifier:#"editVC"];
[editVC setHidesBottomBarWhenPushed:YES];
[self.parentViewController.navigationController pushViewController:newEditVC animated:YES];
//[self.navigationController pushViewController:newEditVC animated:YES];
}
}
Both of the above methods are in the same menu view controller where the button is pressed. I put some break points to test it out and the action sheet delegate method is being called. The if statement is working correctly, but the new VC's are not being pushed onto the navigation controller the way they should. It just runs over those lines and nothing happens. I have also tried setting it up in a way that parent view controller's also have the actionSheet:clickedButtonAtIndex: method in them, but that method is not called.
I believe that it has to do with setting up the actionSheet with an improper delegate, but I don't know for sure.
How can I call this action sheet from one view controller, display it and have actions performed in another? I have it appearing in the correct VC, but the action's aren't working.
Following are the code for displaying alert in a view controller
-(void)saveProducts {
pData = [[JsonModel sharedJsonModel] prodData];
if ([pData count] == 0 && [self respondsToSelector:#selector(alertView:clickedButtonAtIndex:) ] ) {
alert = [[UIAlertView alloc]initWithTitle:#"Alert" message:#"No products against this category" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
[self.tblView reloadData];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0) {
[self.navigationController popViewControllerAnimated:YES];
[actInd stopAnimating];
}
}
But in slow network, alert will come slowly. If we click on back button of navigation bar at the mean time, pop the navigation controller and displaying alert in new view controller. But when i clicks on OK, app suddenly crashes with EXC_BAD_ACCESS error.
I also tried
didDismissWithButtonIndex
function instead of
clickedButtonAtIndex
But same error occurs. Please help me
It works normally if we didn't click on back bar button. Problem only arises when first view controllers alert displays in second view controller
EDIT
This is the error report
* -[ProductsListing alertView:didDismissWithButtonIndex:]: message sent to deallocated instance 0x8478280
EDIT
I understand the problem. When I click on back button, my alert delegate deallocates and delegate calling results error. How can I overcome this?
My best guess is that either 'self.navigationController' or 'actInd' have already been released. Also, your 'UIAlertView' leaks memory (unless you're using ARC). Profile the app using Instruments, selecting the "Zombies" tool and see what it comes up with.
I believe you have to change
[alert show];
to
if(self.view.window){
[alert show];
}
This way the alert appears only if the controller(the view) is still on screen.(why let the user see an alert from a previous screen?)
If you want the alert to appear anyway....then the "old" controller must inform the "new" one that a problem occurred...and now its the new controller's job to inform the user.
Or you can try changin this part
[self.navigationController popViewControllerAnimated:YES];
[actInd stopAnimating];
to
if(self.view.window){
[self.navigationController popViewControllerAnimated:YES];
[actInd stopAnimating]; // im not sure where the animation is...so not sure if this shoulb be in here or not
}
From what you described the problem here may be this(a wild guess)
[actInd stopAnimating];
called after the viewController is removed(popped).the actInd may not have a valid memory and hence it crashes
change the method content like this and check
if (buttonIndex == 0) {
[actInd stopAnimating];
[self.navigationController popViewControllerAnimated:YES];
}