I have two ViewControllers, which aren`t (directly) connected with a Segue. But I want to change a String in the second VC, which i get in the first one. My try was:
#import "secondViewController.h"
#interface firstViewController ()
#property NSString* originalString;
#end
#implementation firstViewController
-(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
secondViewController* svc = [secondViewController new];
svc.anotherString = self.originalString;
}
But it dosent work, because I've only created a instance of the second VC, so the value was not saved. Also I can`t use the Storyboard ID, because I use Xcode 5.
I have a menuVC from which you can get to the firstVC and the secondVC. And from the firstVC I can go back (with the navigationbackbarbutton) to the menu. so: menu->firstVC->menu. menu->secondVC->...->menu
My try with StoryboardID:
-(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
secondViewController* svc =[[UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil] instantiateViewControllerWithIdentifier:#"secondVCSrorybradID"];
svc.anotherString = self.originalString;
}
you can pass the string to second view controller with this code.
secondViewController* svc =[self.storyboard instantiateViewControllerWithIdentifier:#"Your Second VC's Storyboad ID"];
svc.anotherString = self.originalString;
[self presentViewController:svc animated:YES completion:nil];
//you have to create anotherString property in second View Controller's .h File.
now you can get the string of originalString to second VC. Now you can get this value back second VC to first VC.
hope this helps you.
You should pass your data successively through your UIViewControllers navigation. If, for example, you have a navigation like FirstVC > SecondVC > ThirdVC :
In your FirstVC.m, use :
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
((SecondVCClass*) segue.destinationViewController).secondVCString = _firstVCString;
}
With secondVCString being a #property in your second ViewController.
In your SecondVC.m, use :
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
((ThirdVCClass*) segue.destinationViewController).thirdVCString = _secondVCString;
}
With of course thirdVCString being a #property in your third ViewController.
Edit:
As you updated your question, here is what I suggest :
In your MenuVC, add this :
#property (nonatomic, weak) NSString *importantString;
In your FirstVC and SecondVC, add this :
#property (nonatomic, weak) MenuVCClass *menu;
When you push to FirstVC or SecondVC, use prepareForSegue to set the destination's view controller menu property to your menu :
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"FirstSegue"])
((FirstVC*) segue.destinationViewController).menu = self;
else if ([segue.identifier isEqualToString:#"SecondSegue"])
((SecondVC*) segue.destinationViewController).menu = self;
}
In FirstVC or SecondVC, you can change the NSString value from your menu using _menu.importantString = #"";
You should never use new to create a view controller.
If you're using storyboards but not using segues, you can still create a view controller from the storyboard and invoke it.
Use the method instantiateViewControllerWithIdentifier: to create an instance of the target view controller. The set the properties you want to set, and finally make a call to display it (present it modally, push it onto the navigation stack, or whatever is appropriate for your program.)
Related
I have two ViewController (ViewController, ResultViewController).
There have three button in the ViewController,and there have a back button in ResultViewController.
In the ResultViewController I want to get the value from the ViewController passing.
So I add the below code in the ResultViewController
#interface ResultViewController : UIViewController
- (IBAction)backEvent:(id)sender;
#property (weak, nonatomic) IBOutlet UILabel *resultLb;
#property (strong, nonatomic) NSString *selectedVal;
#end
.m
- (void)viewDidLoad {
[super viewDidLoad];
[self.navigationController setNavigationBarHidden:YES animated:NO];
self.resultLb.text = self.selectedVal;
}
- (IBAction)backEvent:(id)sender {
[self.navigationController popViewControllerAnimated:NO];
}
In the ViewController have three button action:
- (void)viewDidLoad {
[super viewDidLoad];
[self.navigationController setNavigationBarHidden:YES animated:NO];
rvc = [self.storyboard instantiateViewControllerWithIdentifier:#"ResultViewController"];
}
- (IBAction)passAEvent:(id)sender {
rvc.selectedVal = #"A";
[self.navigationController pushViewController:rvc animated:NO];
}
- (IBAction)passBEvent:(id)sender {
rvc.selectedVal = #"B";
[self.navigationController pushViewController:rvc animated:NO];
}
- (IBAction)passCEvent:(id)sender {
rvc.selectedVal = #"C";
[self.navigationController pushViewController:rvc animated:NO];
}
When I first click the button A, that can correct pass the "A" value to the ResultViewController.
But when I click the back button in the ResultViewController.
Then click the B or C button action, that still pass the "A" value.
I see the log, that was not enter B, C event.
I try to run again program, when I click the first button with B button, It was correct enter and pass "B" value to ResultViewController.
But when I click the back button, then click the A or C button, it was not correct value in the ResultViewController.It still show "B" value.
I don't know why? how can I resolve the problem if not use delegate or notify?
Thank you.
----- edit ----
To the #Gaurav Singh
I am not find the option about the animate .
It's because you're keeping a strong reference to the UIViewController, via your variable rvc.
You're setting the value of resultLb in the viewDidLoad method of your view controller. When you push the view controller onto the stack, your view controller's setting the label by taking what's stored in it's variable selectedVal.
When you pop the view controller from the stack, because you've got it stored in a variable, it won't get unloaded from memory. It still exists. You may be setting selectedVal again when tapping the second button, however viewDidLoad isn't going to get run again, as it's not getting re-instantiated.
What you could do is overload setSelectedVal on ResultViewController as follows:
- (void)setSelectedVal:(NSString *)selectedVal {
_selectedVal = selectedVal;
self.resultLb.text = selectedVal;
}
This way, when you set selectedVal from the calling view controller, the above method will be run, setting the value of your property in ResultViewController, whilst also modifying your label.
EDIT - Better still:
Rather than setting the value of the label in viewDidLoad:, do it in viewWillAppear: instead. This way, you don't need to override any setters or anything like that. When viewWillAppear is called, it'll read the string you've set and set the label's text accordingly.
Hope this helps!
You are just making it complex. I would do it like this:
First I will connect create a storyboard segue from ViewController to Result Controller (View Controller to View Controller segue) and assign a suitable identifier to that segue (say "resultSegue")
then following code would work
- (IBAction)passAEvent:(id)sender {
[self performSegueWithIdentifier:#"resultSegue" sender:#"A"];
}
- (IBAction)passBEvent:(id)sender {
[self performSegueWithIdentifier:#"resultSegue" sender:#"B"];
}
- (IBAction)passCEvent:(id)sender {
[self performSegueWithIdentifier:#"resultSegue" sender:#"C"];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue
sender:(id)sender {
if([segue.identifier isEqualToString:#"resultSegue"]){
ResultViewController *rvc = segue.destinationViewController;
rvc.selectedVal = sender; //Here SENDER will have value: A,B or C which we have passed in performSegueWithIdentifier:sender: method
}
}
Edit: code in prepareForSegue:sender: method modified so that it check the segue identifier before working on destinationViewController
First, you should give the segue an "id",like "resultSegue".
Then, use the code:
- (void)prepareForSegue:(UIStoryboardSegue *)segue
sender:(id)sender
{
if([segue.identifier isEqualToString:#"resultSegue"]){
ResultViewController *rvc = segue.destinationViewController;
rvc.selectedVal.text = someUILabel.text;
than use the [self.navigationController pushViewController:rvc animated:NO];//.........
}
I will give you more detail when I m at home, if you don`t solve the problem....
Thanks!
put
rvc = [self.storyboard instantiateViewControllerWithIdentifier:#"ResultViewController"];
in viewWillAppear
I have two view controllers inside a Navigation Controller.
In the first view controller I have two buttons. Both of them call the second view controller using a Push segue, but:
I need to know which button sent me in the second view controller. How?
In the second view controller I have a UIDatePicker and a Button "Ok": how can I send the chosen date to the first view controller when Ok is pressed? (And how do I receive them?)
EDIT:
I don't know if my problem is clear: now I know how to pass data from the first view controller to the second view controller with prepareForSegue, but what I really need is to pass data (the picked date) from the second view controller to the first, and how can I do it without a prepareForSegue (when Ok is pressed)?
EDIT2:
I made it. It was so simple, guys...
I decided to use modal segue:
Firstviewcontroller.h:
+(FirstViewController *)getInstance;
Firstviewcontroller.m:
static FirstViewController *instance =nil;
+(FirstViewController *)getInstance
{
return instance;
}
and in its ViewDidLoad:
instance = self;
Secondviewcontroller.m, in the OkButton IBAction:
SecondViewController *secondViewController = [SecondViewController getInstance];
//...
//modify what I need to modify in secondviewcontroller
//...
[self dismissModalViewControllerAnimated:YES];
That's it.
Thank you all anyway.
Assign Identifier to each segue in storyboard and implement
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure your segue name in storyboard is the same as this line
if ([[segue identifier] isEqualToString:#"YOUR_SEGUE_NAME_HERE"])
{
// Get reference to the destination view controller
YourViewController *vc = [segue destinationViewController];
[vc setDelegate:self];
// Pass any objects to the view controller here, like...
[vc setMyObjectHere:object];
}
}
For more info about How to use storyboard and pass value check this article or this discussion on stackoverflow
for the second question you can use delegate pattern
IN SecondViewController.h
#protocol SomethingDelegate <NSObject>
- (void)dateChanged:(NSString *)dateStr; //you can use NSDate as well
#end
#interface ViewController2 : UIViewController
#property(weak) id<SomethingDelegate> delegate;
#end
in .m file
-(void) OkClicked{
[_delegate dateChanged:#"YOUR_DATE_VALUE"];
}
In FirstViewController.h
#import "SecondViewController.h"
#interface FirstViewController : UIViewController<SomethingDelegate>
in .m
-(void)dateChanged:(NSString *)dateStr{
// do whatever you need with dateStr
//also i made some change in prepareForSegue method
}
Note:- take care your naming convenes for VC
just pass the button id to the second viewcontrol.
use delegates to sent the data from second viewcontroller back to first view controller
regards
Johan
I'm currently trying to have a better understanding on how the mechanisms of passing data between controllers work and I'm a little confused especially when passing data back from a second view controller to the main view controller.
This is what I have that works but don't fully understand. I have two view controllers, in the first one I have a button that when clicked it basically goes to the second view controller and a label which shows a message sent from the second view controller. In the second view controller I have a button and a textField, the button basically sends whatever is in the textfield to the label in main view controller.
Here is the code...
// FirstVC.h
#import <UIKit/UIKit.h>
#import "SecondVC.h"
#interface FirstVC : UIViewController <passNames>
#property (nonatomic, strong) NSString* firstNameString;
#property (weak, nonatomic) IBOutlet UILabel *firstNameLabel;
#end
//FirstVC.m
#import "FirstVC.h"
#implementation FirstVC
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier]isEqualToString:#"secondController"])
{
UINavigationController *navController = segue.destinationViewController;
SecondVC *vc2 = (SecondVC*)navController.topViewController;
[vc2 setDelegate:self];
}
}
-(void)viewWillAppear:(BOOL)animated
{
self.firstNameLabel.text = _firstNameString;
}
-(void)setFirstName:(NSString *)firstName
{
_firstNameString = firstName;
}
#end
//SecondVC.h
#import <UIKit/UIKit.h>
#protocol passNames <NSObject>
-(void)setFirstName:(NSString*)firstName;
#end
#interface SecondVC : UIViewController
#property (retain)id <passNames> delegate;
- (IBAction)send:(UIBarButtonItem *)sender;
#property (nonatomic, strong) NSString *firstNameString;
#property (weak, nonatomic) IBOutlet UITextField *firstNameText;
#end
//SecondVC.m
#import "SecondVC.h"
#import "FirstVC.h"
#interface SecondVC ()
#end
#implementation SecondVC
- (IBAction)send:(UIBarButtonItem *)sender
{
_firstNameString = _firstNameText.text;
[[self delegate]setFirstName:_firstNameString];
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
Can someone explain how the prepareForSegue method works in the above code? The reason for this question is because I added an NSLog and it looks like this method is only called in the transition from main view controller to the second controller. Why is this method needed if it is not called when transitioning from second view controller to main view controller which in my case is what I'm doing? It makes sense to use it when passing data from main view controller to a second controller not on the case shown above.
Can some explain the whole mechanism when passing data back to the main view controller?
FYI, I do understand about protocols and delegation.
Thanks a lot.
In your case, you are setting your delegate method of the second view controller to self in mainViewController in you prepareForSegue. This means that apart from navigating to the SecondViewController, you are implementing the callback mechanism in your main view controller, so that your delegate method gets called when the value is passed from the second view controller and this delegate method collects the value as a parameter to handle it in the main View Controller. You might have set the delegate of VC2 as self inn your prepareForSegue because you are creating the instance of VC2 in this method to navigate to the second controller.
Your goal is to hand back the data, like this:
[[self delegate] setFirstName:_firstNameString];
But you can't do that unless you know who to send setFirstName: to, and the compiler won't let you do it unless you guarantee that whoever you are sending setFirstName: to can accept that message.
That is what prepareForSegue prepares. FirstVC has declared that it adopts the passNames protocol, which means that it implements setFirstName:. And now you are saying:
[vc2 setDelegate:self];
...where self is the FirstVC instance. This solves both problems at once. The SecondVC instance (vc2) now has a delegate (the FirstVC instance), it is the right object to send the info back to, and because its delegate is declared as adopting passNames, we know that SecondVC can actually send setFirstName: to that delegate.
Now to the heart of your actual question: The reason for doing this in prepareForSegue is merely that this is the only moment when the FirstVC instance and the SecondVC instance "meet" one another! There is no other moment when the FirstVC instance has a reference to the SecondVC instance so as to be able to call setDelegate on it in the first place. If you weren't using segues and storyboards, the FirstVC would simply create the SecondVC instance directly - and would set itself as its delegate, just as you do:
SecondVC *vc2 = [SecondVC new];
UINavigationController *nav = [
[UINavigationController alloc] initWithRootViewController: vc2];
[vc2 setDelegate:self];
[self presentViewController: nav animated: YES completion: nil];
This is one reason I don't like storyboards: they muddy the story. It's all so simple and obvious when you don't use them and just do everything directly like this.
I have a storyboard project and I would like to pass some data from a view into a tab bar controller, the information will be spread out between the tabs. After doing some research I found a very similar issue: iOS storyboard passing data navigationViewController but the only issue with this solution is that it was not transferring to a tab bar controller. I was wondering would I have to pass the data to each tab or can I pass it to the tab bar controller and then spread it from there? Thank you for your help in advance!
I am currently using the following; but, I get an error:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"FoodPage"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
UINavigationController *nav = [segue destinationViewController];
FoodViewController *destViewController = (FoodViewController*) nav.topViewController;
destViewController.Foods = [foodArray objectAtIndex:indexPath.row];
}
}
In order to get a reference to your UINavigationController in your case, you have to do the following and specify the correct index to your tabBarController first:
UINavigationController *nav = [self.tabBarController.viewControllers objectAtIndex:<THE_INDEX_NUMBER_FOR_YOUR_NAVIGATION_CONTROLLER];
Once you have done so, then you retrieve a reference to your FoodViewController by specifying again the index number for it on your UINavigationController (i.e. if it's on top, then 0):
FoodViewController *destViewController = (FoodViewController*) [self.nav.viewControllers objectAtIndex:0];
Update:
Now that I got a better idea what you would like to achieve:
You are using UITabbarController in which your others controllers are embedded.
Scenario / Example Case:
Let say we had 2 view controllers, controller A and controller B, respectively, both are embedded in a UITabbarController.
What we want:
We are trying to change the text of a UILabel in controller B from controller A.
First, declare a property in controller B in .h:
#property(strong, nonatomic) UILabel *aLabelInControllerB;
Second, declare a a property (or ivar) in your controller A:
#property(strong, nonatomic) ControllerB *controllerB;
Since you are using UITabbarController you don't need to use segue, you could simply
get a hold of UITabbarController via self.tabBarController;
Question: "how would I know then when my tab bar controller is tapped and then change the text of the label in controller B?"
We do this:
Set controller A as the delegate of UITabbarController by:
In controller A .h, add:
#interface <Your_Controller> : UIViewController <UITabBarControllerDelegate>
In viewDidLoad of controller A:
self.tabBarController.delegate = self;
And in controller A .m, implement this method:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
self.controllerB = (ControllerB *) [tabBarController.viewControllers objectAtIndex:1];
//In our example here, we only have 2 view controllers (A and B)
//So, index 1 is where controller B resides.
self.controllerB.aLabelInControllerB.text = #"Hello!";
//This will change the text of the label in controller B
}
And as controller B appears, you will see that the text of the label will be changed to "Hello!"
Setting a NSString in controller B follows the same procedure.
Ex: self.controllerB.stringInControllerB = #"Hi from controller B!";
Hope that helps.
Update 2:
Segue'ing from table view cell to a tab bar controller? Oki.. Here is the solution. I am only using one cell in my example, so should it become desired that you would like to have more cells in the future, I would leave that up to you to adjust.
Let's take a look at the storyboard layout:
In storyboard, control drag from your cell to the tab bar controller.
Add a UINavigationController like in the picture.
In your UITableViewController .m:
Add 2 properties:
#property (strong, nonatomic) UITabBarController *myTabbarController;
#property (strong, nonatomic) YourFirstViewController *myFirstViewController;
Just a friendly reminder:
Remember to add:
self.tableView.delegate = self;
self.tableView.dataSource = self;
Add the following in your table view controller:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
self.myTabbarController = (UITabBarController*) [segue destinationViewController];
self.myFirstViewController = [self.myTabbarController.viewControllers objectAtIndex:0];
self.myFirstViewController.stringFromTableViewController = #"Hi from TableViewController!";
}
And in myFirstViewController, add 2 properties in .h:
#property (strong, nonatomic) NSString *stringFromTableViewController;
#property (strong, nonatomic) YourSecondViewController *secondViewController;
Like from the second edit above, have your first view controller to be still the delegate of UITabBarControllerDelegate and implement this method:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
self.secondViewController = (YourSecondViewController*) viewController;
self.secondViewController.aLabel.text = self.stringFromTableViewController;
}
Just like before, nothing needs to be changed in SecondViewController.
I ran this myself; should be good to go for your setup.
Enjoy.
It's also possible to access data from the TabBar within the TabBar's child viewcontrollers themselves, like so:
MyTabbarViewController *tabbar = (MyTabbarViewController *)self.tabBarController;
NSLog(#"%#", tabbar.data);
This way you only have to set the data in the TabBar like shown above, and then access it whenever you need it in a child view.
I am having two view controllers 'FirstViewController' and 'SecondViewController'. From first view controller it will take input from a text field ,process it and display accordingly in the second view. But I am having a problem while setting label value directly.
#interface SecondViewController : UIViewController
{
NSString *numPlate;
IBOutlet UILabel *output;
};
#property(strong,nonatomic) NSString *numPlate;
#property(strong,nonatomic) IBOutlet UILabel *output;
#end
The main file for FirstViewController.m with prepare for segue is as
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([[segue identifier] isEqualToString:#"Change"])
{
SecondViewController *svc = (SecondViewController *)[segue destinationViewController];
svc.numPlate = input.text;
NumberPlate *numPlate=[[NumberPlate alloc]init];
[numPlate setPlate:input.text];
NSInteger flag=[numPlate checkValidity];
if(flag==0)
{
svc.output.text =#"Invalid License";
}
else
if([numPlate getArea]==NULL||[numPlate getRegOffice]==NULL)
{
svc.output.text =#"Data not found";
}
else
{
svc.output.text =#"VALID License";
}
}
}
But when the action is performed its not working.The label is not changing.
When i used svc.numPlate instead of svc.output.text and in the SecondViewController viewDidLoad method and i used
- (void)viewDidLoad
{
[super viewDidLoad];
output.text=numPlate;
}
Everything is fine with this. Whats wrong in first method??
You will not be able to assign value directly to UILabel of second VC as view is not yet loaded into view hierarchy at this point.
So view cannot render the value assigned prior to it.
On the other hand, holding value in NSString and assigning same on viewDidLoad is working as now your view is in view hierarchy and loaded into memory.
At the time when you push SecondViewController, the SecondViewController's view hasn't been loaded yet, so you can't access its views. You need to create NSString properties in SecondViewController and pass a string to SecondViewController' NSString Object. Then in SecondViewController's viewDidLoad method, use those properties to populate the labels (which will have been loaded by the time viewDidLoad runs).
The initialisation of controller is different and presentation of its view is different process even if the viewController has been initialise its view will not because pushing the controller has not performed and controller dont know he need to load the view.. so we pass the data to controller and it load when view appears... like below code
#interface SecondViewController : UIViewController{
NSString *strMessage;
}
#property(nonatomic,retain) NSString *strMessage;
#end
SecondViewController.m
#synthesize strMessage;
- (void)viewDidLoad {
Nslog(#"%#",strMessage);
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([[segue identifier] isEqualToString:#"Change"])
{
SecondViewController *svc = (SecondViewController *)[segue destinationViewController];
NSString *message = [NSString stringWithFormat:#"Check out %#", nameLb.text];
svc.strMessage=message;
}