How do I cancel a segue that is connected to an UIButton - objective-c

I'm working on this tutorial, which shows how to create an app with storyboards. My viewController has two UIButtons and I have connected a segue to those UIButtons. These segues push a new viewController.
Now I am looking for a way to cancel this segue if a certain condition becomes true. When I used the old xib files to create my interface I was able to do something like this:
-(IBAction)sendButton:(id)sender {
if(TRUE) {
// STOP !!
}
}
This does not work anymore. How can I cancel the push of the new viewController?

Nope. You can't cancel segues that are directly linked to interface elements like your UIButton.
But there is a workaround.
Remove the segue that is linked to the button
Add a segue from the viewController (the one with the button) to the viewController that should be pushed. This must be a segue that is not connected to a interface element. To do this you can start the segue from the status bar. Or create the segue in the left sidebar.
Select this segue, open the attributes inspector and change the Identifier of the segue (e.g. PushRedViewController)
Select Xcodes assistant view, so you can see the .h file of the viewController with the button.
Connect the button to an action. To do this select the button and control-drag to the .h file. Select action in the menu and name your action (e.g. redButtonPressed:)
Open the implementation of your viewController
change the action from step 5 to something like this:
- (IBAction)redButtonPressed:(id)sender {
[self performSegueWithIdentifier:#"PushRedViewController" sender:sender];
}

You can actually do this by adding following method into your code
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
if([identifier isEqualToString:#"btnSegue"])
{
return YES;
}
else{
return NO;
}
}
Lets assume your segue is btnSegue.And if you need the segue to be performed based on some condition you can have following code
if(check)
{
[self performSegueWithIdentifier:#"btnSegue" sender:self];
}
Where check is BOOL which you can set true or false based on your condition.

Related

How to perform IBAction before dismiss sheetViewController from same UIButton?

I am writing a Cocoa Mac app in Objective-C and using Storyboard for my UI.
I have a "Confirm" button in my sheetViewController.m which I want to perform some action (save some settings) as well as dismiss the sheetViewController at the same time. They both use the sheetViewController.m as outlets.
Unfortunately, with Storyboard, I can only pick one received action (IBAction) or dismissController.
I want to perform the IBAction FIRST, before dismissing the sheet. How can I accomplish this?
Happy to do this in code as well instead of Storyboard if necessary!
Thanks!
You can use codes to dismiss controller in your IBAction.
I found out how to reference Storyboard ID in code:
On MainViewController:
- (IBAction)didClickButton:(NSButton *)sender {
SheetViewController *sheetViewController = [self.storyboard instantiateControllerWithIdentifier:#"SheetViewController"];
// Must match Storyboard ID
[self presentViewControllerAsSheet:sheetViewController];
}
On SheetViewController:
- (IBAction)didClickButton:(NSButton *)button {
// Do something
[self dismissViewController:self];
}

Storyboard - Popping to a View Controller then Pushing Causes multiple pushes

In Objective C / iOS;
We have a process similar to this (set up in xcode storyboard);
Menu View Controller -> Enter in a code -> Process/Validate -> Present a Failed Code page
The (->) arrows signify a push segue setup in storyboard
When in failed state I want to pop to the Enter in a code view controller.
UIViewController *vc = nil;
NSUInteger index=0;
for (UIViewController *viewController in self.navigationController.viewControllers) {
if ([viewController isKindOfClass:[SomeViewController class]]) {
vc = viewController;
break;
}
index++;
}
if (vc) {
dispatch_async(dispatch_get_main_queue(), ^(void) {
[self.navigationController popToViewController:vc animated:YES];
});
return;
}
This pops me back to the VC I want to go to.
Except now when I press a submit on the Enter code page it does 3 or 4 more "pushes", when it should only be 1.
Do I need to unwind the segue? I tried emptying the navigational view controller stack, and I even tried ridding it of its last active view controller -- both of these return a blank or black window view frame.
Why would popping a view controller in the navigation stack affect the segues in my view controller to the point where whenever I try to do a push segue action it will try to push multiple view controllers onto the stack?
Turns out I had the following issue
Button press causes segue action in storyboard
I did the same segue action in code on the button, hence pushing multiple times
I have now solved this issue

How to interrupt a modal segue?

I created modal segues with xcode's interface builder using storyboards. So I don't have any code about these segues.I wanna stop one of these modal segues if "BOOL permission = NO". If "BOOL permission = YES" modal segue will work normally. How can I do that ?
You can't interrupt a segue but what you can do is choose whether to call it or not.
Instead of having the segue start from a button (for example) you should have it start from the view controller. Then give the segue an identifier "myModalSegue".
Then connect a method to the button...
- (IBAction)segueButtonPressed
{
if (user.hasPermission) {
[self performSegueWithIdentifier:#"meModalSegue" sender:nil];
}
}
Now that button will only perform trigger the segue if the user has permission.
In storyboard you can give your segues an identifier by clicking on the segue line, going to the attributes inspector and entering an identifier, for example mySegue.
In your code you can then check if the user has permission by connecting your button to an IBAction, and checking the permission bool.
-(IBAction)mySegueButtonPressed {
if (permission) {
[self performSegueWithIdentifier:#"mySegue" sender:nil];
}
}

UIStroyboard segue issue

I created story board App. Actually I have issuw with segue. I have one "A" view controller, and segue is suppose to push "B" viewcontroller. In "A" viewController, we are expecting some userInfo and we are performing some validations for that, and based on validation only it should push new viewController through segue. My issue is, I used triggered segue and it always push "B" view controller. Can you please inform me based on validations, how can I disable segue.
Connect the button from Scene A. Not from a UI Element on Scene A, but from the base view of Scene A.
Now, in Scene A's view controller, if you've got a button that's supposed to validate, just use an if or whatever. If all the info is good, call the segue, if it's not, don't call the segue.
Call the segue like this:
[self performSegueWithIdentifier: #"MySegue" sender: self];
Where #"MySegue" is replaced with the name you gave your segue on the storyboard.
For example:
- (IBAction) myButtonPress:(id)sender {
//validate the info
if(valid) {
[self performSegueWithIdentifier: #"MySegue" sender: self];
} else {
//probably prompt the user that their info is invalid
//maybe clear textboxes, etc.
}
}
They key here though is attaching the segue from the viewController, and not from the button itself. You also have to be sure to name your segue, and be sure the name on storyboard matches the name in code. XCode won't help you autocomplete it.

Should I use the same detail ViewController to work both modally and when pushed?

This seems to be the patten used throughout Apples applications; Creation of a new record is done through a modal View which needs to be saved or canceled to continue, and editing a record is done through a view pushed onto the navigation stack.
It doesn't seem right to be basically duplicating my ViewController for 'add' and 'edit' but there are several differences in how pushed and modal ViewControllers work which complicate things.
How should I be doing this so it can cover both bases?
-
Differences include.
When pushed onto the stack the navBar appears at the top of the View and can be configured to contain the cancel/save buttons. When presented modally this is not the case so to duplicate the interface a toolbar needs to be created separately and close/save buttons added to this instead.
When dismissing a pushed view we send a message to the navigation controller [self.navigationController popViewControllerAnimated:YES];, when dismissing a modal view we send a message to self [self dismissModalViewControllerAnimated:YES];
You could add the UIToolbar in InterfaceBuilder, and then just hide it in viewDidLoad when self.navigationController is not nil.
As for dismissing, you could have something like:
- (void)didCancel {
[self.navigationController popViewControllerAnimated:YES] || [self dismissModalViewControllerAnimated:YES];
}
This will shortcircuit if your viewcontroller is part of a navigationcontrol, and use dismissModalViewControllerAnimated otherwise.
This should work for your cancel button. For your save button, it is useful to call some sort of delegate method such as:
- (void)didSave {
// do your saving juju here
if([self.delegate respondsToSelector:#selector(viewController:didSave:]) {
[self.delegate viewController:self didSave:whatJustGotSaved];
}
[self.navigationController popViewControllerAnimated:YES]; // noop if currently modal
}
In the delegate's implementation then, you can put:
- (void)viewController:(UIViewController*)viewController didSave:(NSObject*)whatJustGotSaved {
// do stuff with parameters
[self.modalViewController dismissModalViewControllerAnimated:YES]; // noop if not modal
}