Xcode Unwind Segues [duplicate] - objective-c

This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 8 years ago.
I am having trouble passing data back between 2 viewControllers. They are linked with a segue and I am trying to link the data generated in the 2nd viewController and use it in the 1st viewController.
The way it works is the user starts at the 1st viewController and navigates to the 2nd viewController which contains multiple settings to choose. When they navigate back to the 1st viewController, I want the data to be passed. With prepareForSegue, it is very simple, but what should I do? Is there a method such as prepareForSegue that works in reverse (i.e going back to the main viewController)? I have tried to use unwindSegues but I am not sure if this is the appropriate method to be using. Can anyone tell me the correct way of setting this up? Or is there a different method I should use? Any help regarding what I should do would be greatly appreciated.

The best way to go about this is to use a delegate. See the articles here and here to learn about delegation. This answer and this one may also help.

You don't have to do anything like that. The unwind method looks something like that:
- (IBAction)unwindToPackerView:(UIStoryboardSegue*)sender
You have access to segue and you can have reference to your source view controller (2 view controller)
- (IBAction)unwindToPackerView:(UIStoryboardSegue*)sender {
// you can check is this right segue if you have more than one:
if ([sender.identifier isEqualToString:#"SegueIdentifier"]) {
CustomViewController *vc2 = sender.sourceViewController;
// get the data from source view controller
}
}
Note that you should add this method to the first view controller.
// Extended
If you want to call unwind segue after button (bar button, etc.) clicked just control drag from the button to the unwind icon (exit icon) in storyboard.
But if you want to run unwind segue programatically (after some action happened, like swipe gesture) you should connect your unwind segue to the view controller (File owner). Simple control drag from view controller (file owner) to the unwind icon in storyboard and choose appropriate segue.
In the code when the action (for example swipe) happened call:
- (void)performSegueWithIdentifier:(NSString *)identifier sender:(id)sender

Related

3 programmatically created UIButtons - how to go between 3 UIViewControllers using those buttons with Storyboard?

Playing around with some Objective-C (well, iOS) and have done the following... loaded some UIButtons programmatically into a UIScrollView. That works well. Though I've always just connected UIViewControllers together using control-click and drag. Now I've created buttons programmatically, I have no idea how to go from one view controller to another in a Storyboard because there is nothing to drag from!
I'm not really sure what code to post as such, because I haven't done anything that /nearly/ works or doesn't work as such. I get how to do it with XIBs. But I suppose the question is : 3 UIButtons have been created programmatically and I have 3 UIViewControllers. How do I access those ViewControllers using my UIButtons?
Thanks
In the Interface builder view control click and drag from the viewcontroller icon under the first view controller, to the middle of the second view controller. A segue will be created, selected the appropriate type.
Now select the segue and in the inspector give it a unique identifier (say 'myNewSegue').
Now in your first viewcontroller you can create a method that has the following code:
-(void)myButtonAction:(id)sender {
[self performSegueWithIdentifier:#"myNewSegue" sender:self];
}
And add this method as a target action to your button:
[myButton addTarget:self
action:#selector(myButtonAction:)
forControlEvents:UIControlEventTouchUpInside];]
A segue doesn't have to have a button at the leading end of it; you can instead draw it from an entire view controller to another. You can also give a segue an identifier, a string that's used as a name for that segue. Once you've done that, you can programmatically trigger that segue by calling -performSegueWithIdentifier:sender:.
To actually call -performSegueWithIdentifier:sender:, though, you'll need to connect the button to a target and action. If you've never done that, read the Event Handling Guide for iOS in your documentation.

Showing Toolbar on 2nd ViewController

I have two view controllers on the same storyboard. What I want to do is send an array of string values to the table view control on another view controller.
ViewController2 *second=[self.storyboard instantiateViewControllerWithIdentifier:#"View2"];
second.arrayFromVC1=areaArray;
[self presentViewController:second animated:YES completion:nil];
The second view controller has a toolbar control at the top by default. (See below.)
Passing data to another view controller wasn't easy for me, who has been using Xcode for two weeks. I somehow managed it. So an array of strings is sent to the 2nd view controller through an array variable (arrayFromVC1) set on the 2nd implementation file. I suppose experienced Xcode users know what I'm talking about. Anyway, the 2nd view controller does receive a list of values as shown below.
Well, the problems are that the toolbar control on the 2nd view controller will disappear when the user gets to see the list and that the table view control (UITableView) occupies the entire window. I understand that you can control the size of the table view control by specifying values under the viewDidAppear method. But my question is... Is that how things work with iOS and Xcode? So if I want to display that toolbar control at the top, I have to do it programmatically by writing code under the viewDidAppear method as well?
Thank you for your advice.
Tom
Tom, are you using interface builder and storyboards? If so, select the ViewController in IB, go to Editor (in the top menu) --> Embed In --> Navigation Controller.
This will embed the chosen VC and any VC it segues to (and so on) into a Nav Controller.

Increasing number of living Views

I've set up a really simple project using storyboards including two views as shown here: http://i.stack.imgur.com/iRx21.png. The navigation can be done by either selecting a cell in the custom table view or hitting the back button labelled with "<<". Everything works fine except the following:
when I switch between the views, every time an instantiation happens. The profiling shows an increasing number of view objects. I would like to keep only one of each view and instantiation should be happen only once. What am I doing wrong? (I'm using ARC.)
Thanks in advance!
You should not link your back button to the parent view controller. This is what causes the new instantiation.
The way to go is to embed the table view into UINavigationController (in IB, choose Editor -> Imbed In -> Navigation Controller. Then change your segue to a Push segue. You can of course hide the navigation bar etc. to make things look exactly as you like. Then, link the back button to the controller with an IBAction and in the handler do a simple
[self.navigationController popViewControllerAnimated:YES];
This would be the appropriate logic of what you are doing. Of course, you can also push the web view modally and then handle the button click with
[self dismissModalViewControllerAnimated:YES];

How to dismiss a modal that was presented in a UIStoryboard with a modal segue?

Setup: I have a storyboard set up, with two simple view controllers A and B. There is a button in A, that transitions to B with a modal segue. B is presented with a modal transition on top of A. It’s fine.
Question: is there a way to pop B away and get back to A with some simple storyboard magic?
Note that if this was all in a navigation controller, and I used a push segue, it would be implicitly be taken care of by navigation controller. There would be a “back” button. There’s nothing comparable for modals, I need to build the UI myself which is fine, but I am wondering if there is a segue mechanic I can use to signal to go back from B to A.
Now the oldskool method to build going back from B to A would be:
create a delegate property on B
set A to be B's delegate when the modal transition segue plays back (I can hook into this using prepareForSegue:sender: in A’s code)
when it’s time to dismiss, B signals to its delegate
A implements a delegate method that dismisses B
This works, but feels like too much overhead and silly.
Is there some UIStoryboard mechanic that I have missed, that would basically do a “reverse modal segue”?
There isn't any storyboard magic for dismissing a modal view controller without writing at least a little bit of code.
But while you do have to implement some code of your own, you don't necessarily have to go to that much trouble. You can just have a button in view controller B that calls [self dismissViewControllerAnimated:YES completion:nil]. (The docs say the presenting view controller should be the one to dismiss, but they also say that the message will be forwarded to the presenting view controller if called on the presentee. If you want to be more explicit about it -- and you'll need to be in some cases, like when one modal view controller is presented from another -- you can explicitly reference the presenter with self.presentingViewController and call dismiss... from there.)
You see the delegate business in some apps because it's one way of notifying view controller A about whatever the user did while in view controller B... but it's not the only way. There's KVO, notifications, or just plain calling A's methods after referencing it with self.presentingViewController (assuming B knows it's always getting presented by A). And if A doesn't need to know about what happened in B (say, because the user hit a Cancel button), there's no need to do any of that -- you can just dismiss the modal and be done with it.
In iOS 6 and later, unwind segues add another option, providing a little bit of "storyboard magic" for dismissing modal view controllers (or otherwise "backing out" of a sequence of segues). But this approach still requires some code -- you can't set it up entirely in storyboard. On the plus side, though, that code provides a path for getting info from the view controller being dismissed (B) to the one that presented it (A).
Apple has a tech note about unwind segues that covers them in detail, but here's the short version:
Define an IBAction method on the view controller class you want to unwind to -- the one that presents a modal view controller, not the modal view controller itself (view controller A in your question). Unlike normal IBAction methods, these should take a parameter of type UIStoryboardSegue *; e.g.
- (IBAction)unwindToMainMenu:(UIStoryboardSegue*)sender
In the presented view controller (B in the question), wire a control to the green Exit icon, and choose the method you defined.
In your unwind method implementation, you can refer to the segue's sourceViewController to retrieve information from the view controller being dismissed. You don't need to call dismissViewControllerAnimated:completion: because the segue handles dismissing the view controller that's going away.
There is storyboard magic to achieve this. It's known as an unwind segue. In A's .h file you implement whatever "target action" style methods you need for however many unwind segues you need. For a modal, it's usually two (cancel and save). So in my A.h file I would add:
// A.h file
- (IBAction)myCancelUnwindSegueCallback:(UIStoryboardSegue *)segue;
- (IBAction)mySaveUnwindSegueCallback:(UIStoryboardSegue *)segue;
Now, in your storyboard, if you have a segue from A to B. You can now do a "target action" style control drag from your cancel/save buttons in B to the green "Exit" icon at the bottom of the B controller in your storyboard. When you do this, Xcode will pick up the two methods we created (since they're in A's header file and they have the correct signature (e.g. IBAction and UIStoryboardSegue *.) and B is the destination of a segue from A) So, there you have it. You have the storyboard magic you were looking for!
In the implementation of the two callbacks, you would have something such as:
// A.m file
- (IBAction)myCancelUnwindSegueCallback:(UIStoryboardSegue *)segue {
UIViewController *modalGoingAway = segue.sourceViewController;
// Do something (like get data) from modalGoingAway if you need to...
}
- (IBAction)mySaveUnwindSegueCallback:(UIStoryboardSegue *)segue {
UIViewController *modalGoingAway = segue.sourceViewController;
// Do something (like get data) from modalGoingAway if you need to...
}
Lastly, if this approach meets your needs, great. You're done. However, I still wire up the whole protocol delegate/dataSource design pattern if "on cancel" or "on save" I want to perform some operations on B's private properties before passing control over to A to remove B from the view hierarchy.

Re-use tableview from different controller: how to split didSelectRowAtIndexPath behaviour?

I'm currently working on my Favorites implementation. In the end, it should work the same as the favorites features in the Phone book on the iPhone.
I've the following set-up (besides other controllers and classes):
TabBarController (named mainTabBarController)
NavigationController with a Tableview (let's call it listNavController)
ViewController with some components for displaying row details (named detailViewController)
NavigationController with a TableView for favorite records (named favoritesNavController)
From the favoritesNavController, I want to select a row (from listNavController) so I can add a new entry to my Favorites tableview.
So, I decided to re-use my listNavController because it has all the functionality I need. Like searching, index, etc.
I've managed to show the listNavController from the mainTabBarController. So no problem here. When I select a row from the listNavController, it displays my detailViewController for that row. Of course, this was expected because that's in didSelectRowAtIndexPath in listNavController.
But, when I launch a listNavController from my favoritesNavController with the help of presentModelViewController, it still shows the detailViewController when selecting a row.
In this case, I want to return the selected row to my favoritesNavController. Then I can add it to my Favorite's list.
So, how do I differentiate this behaviour in code ? Should I use protocols, delegation, etc. ?
Any tips ?
With regards,
Rutger
It turned out that I was looking in the wrong direction.
The solution to the posted question is as follows:
I created a subclass of my listNavController and overrided the didSelectRowAtIndexPath method. Next I presented this new view controller with a navigation controller as a modal view (presentModalViewController).
Finally I set the delegate and a protocol for the subclassed view controller to the initiating class. This way I can present and dismiss the subclassed view controller from the same controller. A much more clean and MVC way to go!