Unwind Segue Programmatically - New twist - ios

Context: I have two VC, A and B. VC A contains several buttons and several labels. When pressing the button in VC A a segue will display VC B/C/ and so forth. Now, when finished with VC A/B/C so forth, the segue is being unwind so that VC A appears. For most of the VC B/C/D so forth, I am using the unwind method which I trigger through a button in that VC (ctrl + drag to "exit" icon). This works perfect, because upon returning to VC A, the following action is being called automatically:
- (IBAction)returned:(UIStoryboardSegue *)segue {
// Here I do some stuff
}
Problem: Now, in one of the secondary VCs (e.g. D), things are a bit special. In this VC I generate some hundred buttons through a loop programmatically, then detect which button is being pressed and finally unwind back to VC A (without a specific button; any of the buttons will trigger the unwind). I know I can do this eg by using this
[self dismissViewControllerAnimated: YES completion: nil]
but this does not trigger the above action when returning to VC A, or by using this
[self performSegueWithIdentifier:#"UnwindSegueIdentifier" sender:self]
but this will generate a new instance of VC A, which I do not want (because labels in the instance of VC A already contains some information).
So, what I want is to be able to return to the same instance of VC A which generated the VC D, and also trigger the "returned" action listed above. Thus, I want to achieve the same effect as when using a button connected to the "exit" icon, but I want to do this programmatically "inside the code" when one the many buttons in VC D is pressed.
Any thoughts?

[self performSegueWithIdentifier:#"UnwindSegueIdentifier" sender:self] should work just fine. Unwind segues do not create new instances of their destinations, no matter how they're performed. They are unique in that it's the only segue that does not create a new instance of the destination.

You can create a unwindsegue by to use programmatically by control+dragging from the scene's view controller icon to its exit icon. After assigning an identifier to this unwindseque, you can call it programmatically.
Reference:
https://developer.apple.com/library/ios/technotes/tn2298/_index.html

Ulas has properly understood and answered the question. The original poster wanted to ensure that the unwind '(IBAction)myCustomName:(UIStoryboardSegue*)segue' selector was called upon unwind without having to 'manually' link a button to the unwind segue. To emphasize this point, assume that you programmatically create a button at runtime based on some condition. You do not have the ability to manually wire that button to the unwind segue. You need to do something like this within VC B...
UIBarButtonItem *dynamicButton = [[UIBarButtonItem alloc]
initWithTitle:#"Cool"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(killView)];
self.navigationItem.leftBarButtonItem = dynamicButton;
- (void) killView
{
[self performSegueWithIdentifier:#"TEST" sender:self];
}
The 'TEST' segue was established from the VC B to Exit manually (i.e the view controller itself not a button). When the button is clicked, it calls the killView method and the segue is triggered via the identifier.

Related

How to close a dialog window in Objective C

I have viewcontroller A and on click of button, i have opened a another viewcontroller with presentation style as Form sheet. In second view controller which is opened have two buttons. Now, when these buttons are clicked, this second view controller should go away and next view controller should open on top of view controller A. But when i am trying below, another view controllers are opening up inside the dialog
i don't know how to unwind this segue and call another scene together.
Nitya, did you try the following piece of code to dismiss current view controller. Use it in the IBAction of the button where you want to dismiss the viewController:-
self.dismissViewControllerAnimated(true, completion: nil)
So you have a root viewcontroller A with a button. If you tap that button, an other viewcontroller (let's name it B) is presented by A as a form sheet. B has two buttons and if you tap one of those buttons, B should be dismissed and after a different viewcontroller (let's say C) shold be presented by A.
Apple don't really recommend this kind of workflow, nevertheless it is possible to implement it. You may shold consider to use navigation controller or master-detail controller instead of this. But if you stick to your original idea here are some possible implementations:
1.
If you are using a storyboard and the button on B triggers an unwind segue to A then you can add this to the end of the unwind seque action:
dispatch_async(dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:#"segueThatPresentsC" sender:nil];
});
2.
If you present your viewcontrollers programmatically then:
Before you present B from A you can configure a button of B so that ist target is A and and its action is a method defined in A:
- (void) dismissAndPresentC {
[self dismissViewControllerAnimated:YES completion:^{
[self presentViewController:instanceOfC animated:YES completion:nil];
}];
}
This second is maybe a bit smaller hack, but still a confusing concept.

Segue from screen with several buttons?

I am trying to write an app using UINavigationViewController. My first screen has several buttons on it, and on the click of each button, I want to segue to a UIViewController. I know that I can add a segue on each button, all pointed to the UIViewController that I want to go to, but I was wondering if it is possible to use only one segue that can be fired from each of the buttons.
If that is not possible, I was wondering if it was possible to open the second UIViewController from the first one, on button click, and provide a Back button like the UINavigationView provides. I did manage to get everything on this idea working, except for the back button. I mean I can put a standard button somewhere on the screen and go back, but I'd like the standard back button on the UINavigationView.
Phew! I'm not sure if that makes any sense.
I know that I could also use a tableview, but I'm trying to set this up with buttons.
Thanks
Edit: Thank you to everyone that answered. I now have this working. I would vote up the answers, but I don't have enough posts to do it. I appreciate the answers!
If you need to have separate action functions for each button, suggest that you segue from the main controller to the other controller and create a segue identifier (see xcode procedure below); then, use performSegueWithIdentifier from each of the button action functions. You can also take advantage of the prepareForSegue. To create the segue, control-drag from the left button in the controller in the storyboard to the controller you want to segue to and pick show.
Check the example code in swift that I did for a very similar problem in the SO reference
Linking View Controllers through button
You can embed the main controller in a navigation controller and that will give you the ability to navigate back. If you have multiple layers you can also use unwind segue.
Link each button to one single action (ex. buttonClick) in that ViewController and then perform the appropriate segue using pushViewController method on self.navigationController
-(IBAction)buttonClick:(id)sender {
if(sender.id == self.button1) {
DestinationViewController *vc = [[UIStoryboard storyboardWithName:#"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:#"VC_IDENTIFIER"];
[self.navigationController pushViewController:vc animated:YES];
}
Or if you already have that 1 segue defined in storyboards you can use
[self performSegueWithIdentifier:#"SegueIdentifier" sender:self];
And use that inside the buttonClick method. Using the 1st example, or the second one as long as the segue you setup in the storyboards is a push then you should already get the back button as that is the default behavior for pushing view controllers onto the navigation stack.

How to unwind segue several steps back?

There are a lot of stuff on the net according the topic but I don't get it. I have an App with Custom segue implementation and without Navigation controller. There are cases in which I need to unwind back several steps.
For implementation I use simple calls:
CODE CUSTOM SEGUE
For wind:
[[self sourceViewController] presentModalViewController:[self destinationViewController] animated:YES];
For unwind:
[[self destinationViewController] dismissViewControllerAnimated: YES completion: nil];
As I understand when I wind segue somewhere in the memory there is data which is used during the unwind. For that reason in the custom unwind I only use read only property destinationViewController.
So how can I unwind several steps back in one action? Or I have to make several unwinds in different View controllers?
I read the following question, but I don't understand implementation.
Bellow I put example of my apps logic:
EXAMPLE
I have 4 VC which is in a chain:
VC A -> VC B -> VC C -> VC D
I wind and unwind back and forth. The logic is ok. There are situation in which I need to unwind back from VC D to VC B. How to do that? Can I unwind directly to VC B or I have to unwind to VC C and then in the unwind handler to unwind to VC B?
I also thought of additional segue from VC D to VC B, but there are remarks on the net that this is not the right way, because segue chain will get messy.
The answer to What are Unwind segues for and how do you use them? has everything you need to know about unwindSegues (I suggest you re-read it).
But for a more direct answer, yes you can unwind back from VC D to VC B. To do that you have to first implement the method in VC B:
- (IBAction)unwindToVCB:(UIStoryboardSegue *)unwindSegue
{
}
after doing so, go to the IB, and control-drag a button's action to the Exit icon, you should then pick the method you created above as the selector. (This is taken directly from the question/answer stated above).
Again, for a clearer version of what I said, read the answer of the linked question I mentioned above.
You have to use UINavigationController, just add it in storyboard as mainViewController and set 'A' ViewController as root view controller for it, then change all segues to push segue, set 'Identifires' for them, and you can use performSegueWithIdentifire to push view controller and inside view controller:
[self.navigationController popViewControllerAnimated:];
or
[self.navigationController popToViewController: animated:];
to dismiss it.

iOS Storyboards: How to segue to the second view with a back button to the first view, but without displaying the first view

Here is my storyboard configuration:
Navigation Controller -> View Controller A -> Push-> View Controller B
^
|
Modal
^
|
View Controller C
What I want to achieve: When a button is pressed in View C, directly View B will be opened modally (No part of View A is to be displayed). Also, View B will have a navigation back button to View A.
To achieve this,
I set up the illustrated storyboard.
I created a segue between View C and the Navigation Controller of View A/B.
In the 'prepareForSegue' method of View Controller C, I get an instance of View Controller A as the first element in the navigation. In this instance, I set a variable like 'directlyProceedToViewB=YES'.
In the viewDidLoad method of View Controller A, I check the variable 'directlyProceedToViewB' and if it is YES, I call 'performSegueWithIdentifier' to segue to View B
The result is so that, first View A is opened modally and after displaying it a very short time, View B is opened with a push animation (View B can navigate back to View A, which is good). But I do not want View A to be displayed any time at all. How can I achieve this?
EDIT:
To better visualize, I'm adding a screenshot with more example cases to support:
Here are some cases I want to support:
We can start with ViewC, click on 'Modally Display B' which opens ViewB, then click 'Back to A' to navigate back to ViewA, then click on 'Dismiss Modal' on ViewA to go back to ViewC
We can start with ViewD, clcik on 'Modally Display A' which opens ViewA, then click on 'PushB' to open ViewB, then go back and forth between A and B and modally dismiss to ViewD.
First of all, some corrections: those are not views but view controllers. And "view A" is not pushed into the UINavigationController but it's the root.
After that, I suggest making the segue in "view C" an unwind segue and implement the IBAction in "view A" by pushing "view B" with [[self navigationController] pushViewController:bViewController animated:NO].
EDIT (adding some details):
I assume that in ViewControllerA's viewWillAppear you present ViewControllerC in a not animated manner.
Implement an unwinding action like (IBAction)unwindAndThenGoToB:(UIStoryboardSegue *)segue in ViewControllerA.
In the storyboard connect the button in ViewControllerC to the Exit icon and select the previously defined method.
Then implement the method with the push call I wrote earlier.
ps: for documentation there is plenty on Apple's website.
Implement this using delegates.Decalre protocol in which class you want and define those methods and call the methods in the view controller you want.There is no many ways of calling some view and showing back button to go different view.modal view is just a concept.and you can use delegate methods to call whatever class you want.
Here I got a way to do so:-
You need to set no animation for segue from viewC to viewA as shown in below image. Then set a segue identifier for segue from viewA to viewB namely, "viewB" and in your viewA .m file add following code,
- (void)viewDidLoad {
[super viewDidLoad];
// Place your conditional check here.
[self performSegueWithIdentifier:#"viewB" sender:self]; //Will directly lead to viewB and viewA won't be shown as no animation is there from viewC to viewA.
}
And your rest flow be like-wise.
I found the solution myself.
First, I discovered that, my original proposal of
In the viewDidLoad method of View Controller A, I check the variable
'directlyProceedToViewB' and if it is YES, I call
'performSegueWithIdentifier' to segue to View B
works as I desired on iOS 7 but does not work on iOS 8.
So the solution is, in the viewDidLoad method of View Controller A, if 'directlyProceedToViewB' is YES, rather than calling performSegueWithIdentifier, use the following code:
ViewControllerB *destVC = [self.storyboard instantiateViewControllerWithIdentifier:#"ViewControllerBStoryboardID"];
[self.navigationController pushViewController:destVC animated:NO];

iOS 6 - programmatically triggering Unwind Segue does nothing

On my app's start up, it programmatically shows a LoginViewController using a segue. The view controller is presented modally with transition set to cross dissolve. Upon successful authentication, I want to dismiss the login view by programmatically triggering an unwind segue. So I added this to my header file:
- (IBAction)unwindSegue:(UIStoryboardSegue *)segue;
now in IB I'm able to control-drag from the "File's Owner" LoginViewController to the Exit button and choose unwindSegue:. This creates a manual segue, it shows up in the Connections inspectors for the File's Owner and the Exit button correctly. I then click on the newly created Unwind segue from the scene in IB and then give it a name. If I click on the "go to" button for the unwind segue action it takes me to the declaration mentioned above.
So far so good, I then trigger this unwind segue upon successful authentication in my GCD block:
....
dispatch_async(dispatch_get_main_queue(), ^
{
[self performSegueWithIdentifier:#"UnwindSegueIdentifier" sender:self];
[self.spinner removeFromSuperview];
self.spinner = nil;
});
.....and nothing happens when it runs. The spinner does get removed correctly, but there's no sign of that unwind segue executing.
A break point in the implementation of unwindSegue: never gets hit. There are no errors thrown. Nothing gets written to the console. The identifier is correct, I triple checked (otherwise it will fail anyway).
I looked at the answers here, here and here but I don't seem to have missed anything.
What I did notice though, is that Xcode thinks unwindSegue: is not linked:
I'm unable to drag from the little empty circle in front of unwindSegue: and link it to the Exit button.
Any help will be appreciated.
If you are using a modal segue to go to your login view, all you need to go back is to call
[self dismissViewControllerAnimated:YES completion:nil];
More precisely, you should call it at the presenting controller (your first controller), but it will be forwarded if you call at at the presented controller. You can use the completion block do any required clean up. There is no need to use GCD.
EDIT
To answer the additional comment: I'm not really sure from your description, but it seems you've implemented the unwind action at the presented controller instead at the presenting controller. Unwind segues are to allow to do something at the caller (e.g., setting data) without an additional protocol.
Quoting text from Apple's Technical Note on Unwind Segue:
To add an unwind segue that will only be triggered programmatically, control+drag from the scene's view controller icon to its exit icon, then select an unwind action for the new segue from the popup menu.
Link to the Technical Note

Resources