UIAlertView send storyboard back to start - ios

iPhone app with a storyboard setup. Button on the initial view that links through to another view. I have added an alert to that button click so that when it is tapped the view changes and immediately the alert appears asking 'Do you want to continue? Yes No'
If they click yes the alert disappears, if they click no the storyboard should go back to the start.
This is what I've got so far and it doesn't work, nothing happens. I've tried multiple various solutions touted on SO but nothing is working so far. Removing the buttonindex check doesn't do anything.
I'd actually prefer this alert to appear even before the view changes but at the moment I have it displaying after the segue occurs which isn't ideal but if I could get it to segue back it would still be acceptable.
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex==1) {
[self.navigationController popViewControllerAnimated:YES];
}
}
any ideas?

I'm guessing you've linked the button on your first view to the second view using a segue on the storyboard. To get it so that the UIAlertView pops up on the first view and then the segue is triggered depending on whether Yes or No is clicked you'll need to delete that segue and instead create it again by Ctrl-dragging from the first view (not the button) to the second view.
Create an IBAction for your button (if you haven't already) by Ctrl-dragging from your button to the views .h file and selecting Action. This should automatically create the method in the .m file - in that method you'll need to call your UIAlertView.
- (IBAction)buttonPressed:(id)sender
{
[myAlertView show];
}
Then the clickedButtonAtIndexMethod should be similar to what you had before. Instead of popping a controller (because the segue hasn't automatically happened) you'll want to call your segue using performSegueWithIdentifier.
- (void)alertView (UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
// They clicked Yes
if (buttonIndex==1)
{
[self performSegueWithIdentifier:#"YourSegueName" sender:self];
}
}
In regards to the clickedButtonAtIndex method not being called have you made the view a delegate of UIAlertView? So in your views .h file you need to have UIAlertViewDelegate like this:
#interface MyViewController : UIViewController <UIAlertViewDelegate>

Related

iOS segue executed twice

I have a table view with different types of table view cells in it. In one of the cells, there are two buttons, which load a view controller when pressed. I am using the following function to handle the button press:
- (IBAction)leftButtonPressed:(id)sender
{
// Getting the pressed button
UIButton *button = (UIButton*)sender;
// Getting the indexpath
NSIndexPath *indPath = [NSIndexPath indexPathForRow:button.tag inSection:0];
// Loading the proper data from my datasource
NSArray *clickedEvent = [[[SOEventManager sharedEventManager] eventsArray] objectAtIndex:indPath.row];
[[SOEventManager sharedEventManager] setSelectedEvent:clickedEvent[0]];
// Everything working as it should up to this point
// Performing seque...
[self performSegueWithIdentifier:#"buttonSegue" sender:self];
}
My buttonSegue is supposed to push a new view controller. Somehow instead of pushing once, it appears to be pushing twice, so I get the following warning:
2013-11-27 01:48:30.894 Self-Ordering App[2081:70b] nested push animation can result in corrupted navigation bar
2013-11-27 01:48:31.570 Self-Ordering App[2081:70b] Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.
In my case it leads to a crash, since there is an event in which I want the app to immediately pop the view controller so it an go back to my table view. I use an alertview for this and handle the event with the following:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *buttonTitle = [alertView buttonTitleAtIndex:buttonIndex];
// ...
// Additional checking of button titles....
else if ([buttonTitle isEqualToString:NSLocalizedString(#"Vissza", nil)])
{
[self.navigationController popViewControllerAnimated:YES];
}
}
It might me interesting to note that I have an other segue from my "regular" table view cell, and in that case I use the prepareForSegue: method
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"detailSegue"])
{
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
SOEvent *selectedEvent = [[[SOEventManager sharedEventManager] eventsArray] objectAtIndex:indexPath.row];
[[SOEventManager sharedEventManager] setSelectedEvent:selectedEvent];
}
}
In this case the view controller gets pushed perfectly, and even popped immediately if that is required. I am testing this on iOS7 and Xcode 5. I haven't encountered a problem like this before, any help would be greatly appreciated.
Maybe you want to wire up your segues with View Controllers instead of UIButtons!
You should probably have the segues wired with some button like this:
But you should wire them with the view controllers instead:
Answer by can poyrazoğlu:
are you sure you've wired up the actions correctly in interface
builder? maybe you've wired the event for, say, both touch up inside
and touch down inside instead of just touch up inside. or maybe you've
also assigned the segue from both code and again in interface builder.
have you checked them? it's a common mistake. –
I was assigning the touch up inside actions for each button both the storyboard and my tableview's datasource methods.
Thank you for your quick help can poyrazoğlu!!
For Swift 3, xcode, and ios 9+, which is what I am using: Make sure you are drawing segue from your UIViewControllers and not buttons or other interfaces.
I had the same problem, and simply changing the start of the segue from the UIController instead of the button removed this bug.
I always get this issue when I have my buttons directly wired up to the destination view controller. You need to make sure that you first delete the old segue you made, then click on the present view controller (where you are coming from) and CTRL + click to destination controller.
This should fix it :)

How to call method on presenting view controller from modal view controller

I have a modal view controller that I called in from another view controller. Upon dismissal of the modal view controller, I want a method to be called on the view controller that presented that modal view. What is the easiest way to do this?
I tried doing this in my modal view controller: [(ParentViewController*)self.presentingViewController foo]; before calling [self dismissViewControllerAnimated:YES completion:nil];.
Xcode gives me an error saying foo isn't recognized, even though it is defined and prototyped in the controller. If your solution involves blocks, I really don't understand them so I would appreciate it if you would add more detail. Thanks.
ParentViewController.h
#interface ParentViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource>
{
NewAssignmentViewController *newAssignmentViewController;
TableViewDataSource *data;
}
-(void)foo;
#end
You need to get a correct reference to your presenting controller like this:
ParentViewController *presenter = [(UITabBarController *)self.presentingViewController viewControllers][0]; // 0 is assuming that ParentViewController is in the first tab. Change if necessary
[presenter foo];
The other way to do it would be to use delegation, but that's an answer for another time.
If you are using a Storyboard segue to present your view controller you could dismiss it using an Unwind Segue. An unwind segue is a special kind of segue that unwinds the presented view controllers back to a presenter.
To accomplish this, you would create a method in the presenting view controller with the following signature:
- (IBAction)unwindAction:(UIStoryboardSegue*)unwindSegue;
This is different than a standard IBAction because the parameter type is a UIStoryboardSegue* instead of the normal id type (it doesn't have to be named unwindSegue:, it could be modalViewFinished: or whatever you like - the important part is that it has a return type of IBAction and a parameter type of UIStoryboardSegue*).
Once you have this method defined, in your storyboard you control-drag from the modal view controller icon (below its view, in the little bar of icons) and release the connection on the green exit sign. This will create an unwind segue, which you should give an identifier in the attributes inspector. Unwind segues will not show up visually in the storyboard canvas, so you will have to find it in the list of items on the left side of the canvas (this is collapsed by default, expand it by clicking the little circular button in the lower left hand corner of the canvas).
Once you've done that, rather than calling [self dismissViewControllerAnimated:YES completion:nil], just call [self performSegue:<Identifier you gave the unwind segue>] instead. During this process the unwindAction: method defined on the presenting view controller and the prepareForSegue: method on the modal view controller should be invoked. You can do whatever cleanup you need to do in these methods (calling the foo method from unwindSegue:, for example).
You call the method on the UIViewController that is your MainView, and pass it your UIViewController you want to be the ActionSheet.
UIActionSheet *actionSheetController =[[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:nil otherButtonTitles:#"show otherview", nil];
[self presentModalViewController:actionSheetController animated:YES ];
To dismiss the UIActionSheet, dimissWithClickedButtonIndex:animated: is a method for the UIActionSheet that you can implement. The method can be called by whoever (so if you want to dismiss it from your mainview have a reference to the action sheet and do something like
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
{
switch (buttonIndex){
case 0:
{
[actionSheet dismissWithClickedButtonIndex:0 animated:YES];
}
break;
case 1:
{
MyClass *myclassObject = [[MyClass alloc]init];
[myclassObject foo];
}
}
}
The method is also called whenever the 'cancel' button is clicked by the user.
Use this ModalViewControllers link for better understanding...!

Navigation controllers toolbar goes blank after showing a UIActionSheet from it

In an iOS run on iOS 6.1 (emulated) and 6.1.3 (physical device) application we show an UIActionSheet from a view with the following code:
UIActionSheet *actionCreateNewComment = [[UIActionSheet alloc] initWithTitle:CLocalised(#"EditExistingComment") delegate:self cancelButtonTitle:CLocalised(#"No") destructiveButtonTitle:nil otherButtonTitles: CLocalised(#"Yes"), nil];
[actionCreateNewComment setActionSheetStyle:UIActionSheetStyleBlackOpaque];
[actionCreateNewComment setTag:ActionSheetTagNewComment];
[actionCreateNewComment showFromToolbar:self.navigationController.toolbar];
[actionCreateNewComment release];
The View sets the toolbar with:
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithCustomView:control];
[self setToolbarItems:[NSArray arrayWithObject:item]];
Where control is an UISegmentedControl.
The code for handling the button actions are:
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{
bool shouldAddNewComment = buttonIndex == [actionSheet cancelButtonIndex];
[self navigateToCommentScreen:shouldAddNewComment];
}
Every time this action sheet is shown the tool bar will go "blank". Not only for this view but also for the other views.
Here is an example of a work flow:
Note that if I navigate from Instr to Comment without the UIActionSheet, but otherwise the same code, the problem will not appear.
The problem here is that in the "Instr" view the toolbar is empty.
If I pop two more views to return to "List" its toolbar will be empty as well:
Even if I then navigate back to WO and Instr, by allocing and init new forms, and then pushing them, the toolbars will still be empty.
There is another navigation option from the "List" View that shows a "Summary" view. Depending on the data this view will show with or without buttons in the toolbar:
Both "Summary Button" and "Summary empty" are intended views. "Summary hidden" is shown after the UIActionSheet have been shown before navigating between Inst and Comment.
But if I navigate to a "Summary" view that is shown as "Summary Empty" in the picture (intended, due to the data) then the toolbar will start working everywhere again. Or at least until I show the UIActionSheet from the Instr view again.
There are some, in my opinion, strange things about this:
If I navigate to a view where the toolbar is empty, which hides the toolbar, see pic: "SummaryEmpty", then the toolbar will show as intended in the other views.
We use the same code to show an action sheet in other views of the application without any problem
If I run the application on an emulated iOS 5.1 or emulated iOS 5.0 the problem does not appear
Have I missed something when it comes to dismissing the action sheet or how to show it?
Please let me know if you want more information.
Update:
If I forcibly call [self setToolbarItems:nil] after the action sheet button event and then update it with the same items as before the Tool bar will show up as intended.
This does not feel as the right way to do it.
Update 2013-05-28:
Thanks to #LOP_Luke I found out that if I do not navigate to the "Comment" view from -(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex but just returned the tool bar will start working again.
So, can there be some kind of problem when I navigate to another view when then clickedButtonAtIndex is still on the call stack?
Update 2013-05-29:
If I add a method:
-(void)commentScreenYes{
[self vanigateToCommentScreen:YES];
}
And change the button code to:
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{
bool shouldAddNewComment = buttonIndex == [actionSheet cancelButtonIndex];
if(shouldAddNewComment){
[self performSelectorOnMainThread:#selector(commentScreenYes) withObject:nil waitUntilDone:NO];
return;
}
[self navigateToCommentScreen:shouldAddNewComment];
}
Then it will work for the cancel button, but not for the yes button. The cancel button will also make the toolbar work again after it have been "broken" by the yes button. If I pipe both buttons through this flow (by adding a new method) they will both work.
And still, if I run it at iOs 5.1 it will work no matter which flow I choose.
The – setToolbarItems method is a UIViewController method specific to each child view controller of your navigation controller. Assuming [self navigateToCommentScreen:shouldAddNewComment] pushes a new view controller onto the navigation stack you will have to set the toolbar items for the new view controller too as a separate call, regardless of what the action sheet is doing. For example,
-(void) navigateToCommentScreen:(bool) shouldAddComment{
CommentViewController* viewController = [[CommentViewController alloc] init];
[self.navigationController pushViewController:viewController animated:YES];
[viewController setToolbarItems:self.toolbarItems];
}
Otherwise the new view controller will have no toolbar items and the toolbar will be blank.
Edit
I created a navigation controller with a root view controller that has a toolbar with a UISegmented control. I pushed a new view controller onto the navigation stack via a UIActionSheet delegate method and then popped it back. The toolbar was exactly how it should be on the root view controller (the segmented control faded back in as expected). You must be doing something more complicated with your controller with regards to setting the toolbar items that is causing them to disappear. Without seeing your viewController's source methods it's hard to tell what the problem is. Make sure that you are not setting your toolbar items to nil or resetting them with an empty array somehow.

IOS: Stay on the first view controller before complete all controls (StoryBoard)

I connected first view controller with the second one using StoryBoard Push Segue and Interface Builder.
The button is named GO on top/right.
I have three textfield that must be filled before going to second controller.
I display an alert when one of them is empty.
The problem is that my code after displaying correct alertView goes to SecondController instead of remaining on mainController.
if ([segue.identifier isEqualToString:#"DataDisplay"])
{
if (![self verifySelection]) {
return;
} else {
RowViewController *rowViewController = segue.destinationViewController;
// rowViewController.delegate = self;
}
}
1) You have a segue wired directly from your Go button to your Sensor Data view controller. You don't want this, because anytime someone touches Go, the segue is going to happen ... no stopping it. So, first step is to remove the segue you have going from Go to your second view controller.
2) Instead, wire the segue from the File's Owner icon below the view controller to the second view controller. Give it a name like DataDisplay.
3) In the IBAction for your Go button
if ([self verifySelection) {
[self performSegueWithIdentifier:#"DataDisplay" sender:self]
}
An easy fix would be to create the segue manually, rather than letting the interface builder manage it. So you would ctrl-drag from your main view controller to your second one, selecting push as the type of segue and assigning it an identifier through the identifier inspector, then you connect an IBAction to your Go button and in the method you perform the checks on the text fields before programmatically firing the segue with:
[self performSegueWithIdentifier:#"whateverIdentifierYouGaveYourSegue" sender:self];
Heads up: to create a manual segue from a viewcontroller to another one, you need to either zoom out in your storyboard or ctrl-drag from the yellow circle underneath the view!
Edit: Your IBAction connected to the button method should be something like the following:
- (IBAction)download:(id)sender {
if(text boxes are ok)
[self performSegueWithIdentifier:#"segueIdentifier" sender:self];
else
[self showWarning];
}
Make sure that you assigned the ID segueIdentifier to the segue you created in your storyboard.
Your problem is you are defining the "performSegueWithIdentifier" after displaying the alert.
I think the code you are doing is like this :
//AlertView Allocation
[alert show];
Perform Segue
If this is how you are doing, then you are doing it wrong.
You have to use the structure of If-Else Statements and put up the Perform Segue in the condition where all the textfields are filled.

iOS - Create an Popover View using StoryBoard

Hi there, Now I'm trying to create a Pop-OverView using an Xcode
storyboard. Firstly, I have
rootViewController, UIViewController, and UITableViewController
I want the UIView to act as a page flip and the UITableView will show popOver under the navigationBar item controller.
For the UITableView, I want to make a Pop-Over under NavigationBar controller. The problem is, when I touch the Navigation item to show the UITableViewController, it shows correctly, but when I try to close the Pop-Over View, it won't close. And then, the navigation item doesn't work well. It shows multiple instances of popOverView when I touch it multiple times.
This doesn't seem to make sense to me. Can anyone help me out or tell me where to find documentation / tutorials on this?
UPDATE:
For the UIPopOverController, it seems to work well now, but it is still bugging me when I touch a Navigation Item multiple times. It will show multiple instances of PopOver. How can I handle it, so it will show only one instance?
I had the same problem and mostly found the solution here. Basically you change the action of the button each time it's pressed to either display or dismiss the popover. Here's the code I ended up with:
#interface FilterTableViewController : UITableViewController {
UIPopoverController *editPopover;
id saveEditSender;
id saveEditTarget;
SEL saveEditAction;
}
-(void)prepareForSegue:(UIStoryboardPopoverSegue *)segue sender:(id)sender{
if([[segue identifier] isEqualToString:#"EditFilterSegue"]){
// Save the edit button's info so we can restore it
saveEditAction = [sender action];
saveEditTarget = [sender target];
saveEditSender = sender;
// Change the edit button's target to us, and its action to dismiss the popover
[sender setAction:#selector(dismissPopover:)];
[sender setTarget:self];
// Save the popover controller and set ourselves as the its delegate so we can
// restore the button action when this popover is dismissed (this happens when the popover
// is dismissed by tapping outside the view, not by tapping the edit button again)
editPopover = [(UIStoryboardPopoverSegue *)segue popoverController];
editPopover.delegate = (id <UIPopoverControllerDelegate>)self;
}
}
-(void)dismissPopover:(id)sender
{
// Restore the buttons actions before we dismiss the popover
[saveEditSender setAction:saveEditAction];
[saveEditSender setTarget:saveEditTarget];
[editPopover dismissPopoverAnimated:YES];
}
-(BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController
{
// A tap occurred outside of the popover.
// Restore the button actions before its dismissed.
[saveEditSender setAction:saveEditAction];
[saveEditSender setTarget:saveEditTarget];
return YES;
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// Before we navigate away from this view (the back button was pressed)
// remove the edit popover (if it exists).
[self dismissPopover:saveEditSender];
}

Resources