I have a question regarding storyboard segues. Platform is iOS 6.1. Target device is iPhone. IDE is Xcode 4.5.2. What I have are 2 TableViewControllers, both of which have a segue (segOne & segTwo) to the same ViewController. The desired outcome is that when a user navigates to this ViewController, they will return to the originating TableViewController. To do this I need some way to pass the id of the originating segue (or TableViewController) to the destination ViewController. In the past I accomplished this by setting up the 'back' button in the NavigationItem which always worked fine. Now, however I want to code the back buttons as separate controls.
I understand that to accomplish this I need to set up the 'prepareForSegue' method in the source TableViewControllers which I have done. Where I am having a problem is writing the code in the destinationViewControllers to programmatically identify which of the two segues is the source segue, segOne or segTwo? Both segues are identified as 'segOne' and 'segTwo' in Storyboard Seque. Both are style 'push' segues and both are destination 'current' segues. Each of the TableViewController implementation files are identical except for the segue ids. Here's my code:
header file:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender;
implementation file:
-(void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender
{
if([[segue identifier] isEqualToString:#"segOne"])
{
NSLog (#"TableViewController1: segue.identifier %#", segue.identifier);
NSLog (#"TableViewController1: self %#", self);
NSLog (#"TableViewController1: sender %#", (id)sender);
}
}
The output is exactly what I would expect. Here it is forTableViewController1:
TableViewController1: segue.identifier segOne
TableViewController1: self <TableViewController1: 0x928eb70>
TableViewController1: sender <UITableViewCell: 0x927a3e0; frame = (0 22; 768 44); text = ' 1. birth month and year...'; autoresize = W; layer = <CALayer: 0x928d380>>
So, what I need is the code for the ViewController that identifies which of the two segues, segOne or segTwo is the originating segue. I do understand that any code needs to go in the viewDidAppear method of the destination ViewController. Once I have this it will be a simple matter to code the IBAction in the ViewController to navigate to the originating TableViewController. Any code examples on how to do this would be appreciated. thx...
When you push a view controller using a Navigation view controller, it will be pushed on the top of a stack with view controllers.
So all you have to do get back to the previous view controller is to pop the one that got pushed.
Put this in your IBAction method:
[self.navigationController popViewControllerAnimated:YES];
Now, if you really want to know what segue that was used, you can just pass the segue identifier to the destination controller, and put it in a property like this:
-(void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender
{
if([[segue identifier] isEqualToString:#"segOne"])
{
MyOtherViewController *other = (MyOtherViewController *)segue.destinationViewController;
other.theSegueIUsed = #"segOne";
}
}
You also need to add the property to the other view controllers header file:
#property (strong, nonatomic)NSString *theSegueIUsed;
Related
how do i pass a value to a table view controller that has a navigation controller?
i have code to push the new view controller and make it active but no way of passing a value to the table view.
UIViewController *newTopViewController = [self.storyboard instantiateViewControllerWithIdentifier:identifier];
the problem is that i dragged a navigation controller onto the storyboard and it came attached to a tableview. i linked that tableview to my custom tableview class. When i instantiate the view controller from the name "nav", which is the storyboard id of the navigation controller, i get the navigation controller as the view controller being instantiated (newTopViewController); so how do pass a value from where i instantiate the view to the tableview controller?
I know this is already answered (nicely done ttarules), but I thought I'd pass along some extra comments. It's very common to have some type of view controller embedded in a navigation controller. Wrapping a standard view controller in a navigation controller then doing a modal segue to it, gives you a modal scene, with a nice nav bar to put buttons in, etc. Also on the iPad, replace segues can commonly use navigation controllers. It's all about how you design things, but if you know how the controllers stack up and how to easily reference them, you will have more design options. Here is a snippet you can use in your prepare for segue method to easily detect it.
if ([segue.destinationViewController isKindOfClass:[UINavigationController class]]) {
//note: have to get reference to next vc through nav controller
self.nextViewController = [[(UINavigationController*)segue.destinationViewController viewControllers]lastObject];
}
else {
self.nextViewController = segue.destinationViewController;
}
You should pass it inside prepareForSegue if your already going to be segueing to that VC.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([[segue identifier] isEqualToString:#"whateverYourSegueIsCalled"])
{
YourTableViewController *tableView=(YourTableViewController*)segue.destinationViewController;
tableView.anyPublicProperty = yourValue;
}
}
EDIT:
Okay so since you actually just have the tableView embedded, you simply need to access the viewControllers property of the navigationViewController..
So it will be something like this inside your navigationViewController class:
YourTableViewController *tableView = (YourTableViewController*)[self.navigationController.viewControllers objectAtIndex:0];
tableView.yourPublicProperty = whateverValueYouWant;
-Your tableView will be the object at index 0 unless you have other VC's also embedded..So you can just print out your vc's and then figure it out from there if you do.
I am using storyboard for making iPhone app.
I placed 2 buttons on the main viewcontroller, and add actions to present modal new TestViewController on the Storyboard.
When click 2 buttons, same TestViewController will be shown, but I want to know which button is clicked in the TestViewController viewdidload.
Any advice or help?
what #Richard Brown suggested is good.
Assign tag to buttons and use that as below:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"ChooseAPlayer"]) {
TestViewController = [segue destinationViewController];
TestViewController.variablename = buttontag;
}
}
Assign different segue names to each button and in the prepareForSegue method you can use that information to pass what you want to the new ViewController.
I have view which contains a collection view. I have added this collection view programatically (in viewDidLoad) - so that is not on the storyboard. This collection view contains several cells. When a user clicks on one of the cells in the collection view, I want to segue to a different view controller (that I plan to add on the storyboard). My question is - since the collection view is not on storyboard, how can I segue out of that? Or is there other way to accomplish that.
Some updates to the original question above:
In Parent View Controller, I do the following:
//Layout for the collection view
CDLayout *cdLayout = [[CDLayout alloc] initWithItemSize:CGSizeMake(cardWidth, cardHeight) withItemInsets:UIEdgeInsetsMake(topInset, leftInset, bottomInset, rightInset) withInterItemSpacingX:8.0f withTopMargin:margin withLeftMargin:margin withBottomMargin:margin withRightMargin:margin shouldRotate:NO];
//This is the collection view controller
CDLineLayoutViewController *cdLineVC = [[CDLineLayoutViewController alloc] initWithCollectionViewLayout:cdLayout withItemCount:12 ];
// add the collection view controller to self - the parent
[self addChildViewController:cdLineVC];
[cdLineVC didMoveToParentViewController:self];
// add collectionView as a subview
[self.view addSubview:cdLineVC.collectionView];
The collectionView has 12 cards. When a user clicks on one of the cards I want to move to another view controller.
As you can see the collection view is not on the storyboard. So, is there a way to create a segue?
BTW, one option is what Taseen suggested below. I tried that and it is working. But as I understand it is not actually a "segue."
Can you please show us any code you have written
What i have understood from your question is that you have added collection view in viewDidLoad. and i believe you have set the delegate of collection view to self, So in
didSelectItemAtIndexPath method you can write this code
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
//From indexpath.item, you know which cell was clicked
//using switch method
switch (indexPath.item) {
case 0:
//You Can push the view controller from here
//You need to import the header file of that View controller
DestinationViewController *destinationView;
//if the self controller in which the collection view is shown is embedded in Navigation controller,
//you can push using
[self.navigationController pushViewController:destinationView animated:YES];
//if it is not embedded, use modal segue
[self.modalViewController presentModalViewController:destinationView animated:YES];
break;
default:
break;
}
}
EDIT: The segue you will create on the storyboard from your ParentViewController to DestinationController will have segueIdentifier property. like shown below
then in didSelectItemAtIndexPath instead of pushing the controller you can use this code
[self performSegueWithIdentifier:#"collectionViewSegue" sender:self];
you can also configure the destination view controller using prepareForSegue method..
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
DestinationViewController *targetVC = (DestinationViewCOntroller *)segue.destinationViewController;
//if you pass anything you can do it here
//if to set any public variable for example image for the imageview
targetVC.cardImageView.image = [UIImage imageNamed:#"queen.png"];
}
This method will be in your parent controller.
Create a new segue by ctrl-click on the initial viewController and drag to the destination viewController. Don't forget to set the segue identifier.
than overwrite the -(void)performSegueWithIdentifier:(NSString *)identifier sender:(id)sender method.
I have a very simple application that has only 2 view controllers. The initial root controller has two UIButtons on it, and they both transition to the 2nd view controller via Storyboard. The information that is loaded on the 2nd view controller is determined by a buttonPressed IBAction that is linked to both the initial view controller. The problem I am having is that once I tap one of the two buttons, I get the 2nd view controller loading up blank. From debugging, I notice that the buttonPressed function is called after the 2nd view is loaded. How can I make it so that the buttonPressed function is called BEFORE the 2nd view controller is loaded blank?
Hardly any code involved, I have both UIButtons tagged 1 and 2, and the button press function loads two text files depending on the sender tag equaling 1 or 2. Everything is linked up in Storyboard. Any suggestions?
*buttonPressed function:
if([sender tag] == 1)
count = 1;
else
count = 2;
The count determines the file to be loaded.
*In Storyboard, I have just 2 view controllers, (No navigation or any of that) and two UIButtons on the initial view and a text view on the second to display text. The UIButtons are connected to buttonPressed and to the 2nd view controller segue.
Don't bother with a IBAction for button pressed. Have the buttons segue to the new controller and use prepareForSegue:sender: to figure out which button was tapped (the sender).
You can then get a reference to your controller that's about to appear with something like...
SecondController *next = (SecondController *)[segue destinationViewController];
...and tell it which file to load, which it can do in viewDidLoad:.
This sounds like a case for prepareForSegue!
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([[segue identifier] isEqualToString:#"First Button Segue Identifier"])
{
ViewController *theViewController = segue.destinationViewController;
theViewController.textProperty = self.firstButtonText;
}
else if ([[segue identifier] isEqualToString:#"Second Button Segue Identifier"])
{
ViewController *theViewController = segue.destinationViewController;
theViewController.textProperty = self.secondButtonText;
}
}
There you go, it is pretty generic because I didn't know your variable names or what text you are using. Similarly, if the segues don't have seperate identifiers, you can use the sender to determine which button is being pressed.
Good luck!
I need some help in picking the 'right' solution for the following 'problem'.
I use the new storyboard feature to link all the screens of my application together. Basically the structure drills down to:
[Navigation Controller] => [View Controller #1] => [Tabbar Controller] => [View Controller #2]*
**(and some other tabs which are for now not important)*
I have attached a segue (push) from the first View Controller (#1) to the View Controller behind the Tab Bar Controller. This push is triggered when the users presses something on the first controller and works fine.
// Execute preset segue
[self performSegueWithIdentifier:#"segueEventDetail" sender:self];
When the user (which is now in the View Controller #2) presses the back button in the navbar the user goes back. Suppose he now triggers the segue again, the second view controller is shown again but is now 'resetted' (empty). (I believe after reading several fora and articles this is standard behavior when using segue's because these destroy and reinitiliaze the view controller's every time?)
This (the view controller being resetted) poses a problem because the contents of the second view controller is dynamic (depend on a JSON response from the server) and thus it is 'needed' that the view controller remains intact (or is restored) when the user comes back.
I have found several sources (see bottom) describing the same issue, but the solutions vary and I need some help picking the right one.
Summarize:
How can I 'retain'/save the state of a View Controller when the users presses back, while preserving the use of Storyboard & preferably also Segue's
Own Thoughts:
#1 I'm now thinking of caching the JSON Response to my singleton class (and from there to a PLIST) and checking within the second view controller if this data is present and than rebuild the view after which I check for any new data (resume normal operation).
#2 Another one I'm thinking of is 'bypassing' the segue and manually handle the switch of views , partially explained in (Storyboard - refer to ViewController in AppDelegate) - Is this also possible?
But maybe there is an easier/better option?
http://www.iphonedevsdk.com/forum/iphone-sdk-development/93913-retaining-data-when-using-storyboards.html
Storyboard - refer to ViewController in AppDelegate
How to serialize a UIView?
Yess!! I got the solution. Do the following:
In you're .h file:
#property (strong, nonatomic) UITabBarController *tabController;
In you're .m file:
#synthesize tabController;
tabController = [self.storyboard instantiateViewControllerWithIdentifier:#"tabbar"];
The selected index is the tab you want to go
tabController.selectedIndex = 1;
[[self navigationController] pushViewController:tabController animated:YES];
For anyone coming across this (my) question in the future, this is how I ended up 'coding' it.
Open the storyboard and select your 'Tab Bar Controller' and open the Attributes Inspector
Fill in an 'identifier' in the field
With the first view controller (see scenario in original post) I create an global reference to the viewcontroller:
firstviewcontroller.h
#interface YourViewController : UIViewController {
UITabBarController *tabController;
}
firstviewcontroller.m
//Fill the reference to the tabcontroller using the identifier
tabController = [self.storyboard instantiateViewControllerWithIdentifier:#"tabbar"];
Now to switch from the firstviewcontroller the following line can be used:
[[self navigationController] pushViewController:tabController animated:YES];
This might be even more simple solution (without using properties - in fact, all your class instances don't need to know about their destination controllers, so just save it as static in the pushing function):
static UIVewController *destController = nil;
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
if (!storyboard) {
DLog(Storyboard not found);
return;
}
if (destController == nil) { //first initialisation of destController
destController = [storyboard instantiateViewControllerWithIdentifier:#"{Your Destination controller identifyer}"];
if(!destController) {
DLog(destController not found)
return;
}
}
//set any additional destController's properties;
[self.navigationController pushViewController:destController animated:YES];
p.s. DLog is just my variation of NSLog.
But it's really interesting how to do this with segue?