I'm working on an app using storyboard and Navigation controller is embedded in. In the child view, when a button is pressed, it calls a function in parent view controller without changing the view.
[self.delegate buttonPressed];
and the method supposed to update the text of textview in child view.
childviewcontroller.textViewName.text=#"something";
But the textview is not being updated.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
[[segue destinationViewController] setDelegate:self];
}
Long story short is, if I want the parent view controller to change the text of a textview in child view controller, how should I set it up when I'm using storyboard.
Can anyone give me some ideas?
I don't know if I explain it clearly or not. I'm still kinda new to this and am learning while making this app. Thank you in advance.
This sounds confusing. If you are needing to fire off a function in the parent VC, why not create a class method that returns what you need and then do something like this in that child VC:
NSString *newText = [MyParentViewController getSomeData];
myTextView.text = newText;
OR even better, put that class method in a new class that gets data for your VC's. This would be more in-line with object oriented programming.
Related
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"CreateUser"])
{
CreateUserViewController *createUserVC = segue.destinationViewController;
createUserVC.email.text = #"something from fb";
createUserVC.age.text = #"99";
createUserVC.name.text = #"Name from fb";
}
}
I call this segue from my "StartVC." I have another CreateUserViewController which contains different textfields to hold user details. I have no idea why this wouldn't be working, can someone pls enlighten me?
By breakpoints I can see the code is run, and the create user vc is presented, but the fields are empty(only containing placeholder; I do not write the strings blank by = #"" or anything alike)
You should not try to make changes to another view controller's view objects.
That violates the principle of encapsulation, an important part of object-oriented design. (It means that other parts of your code, outside of your view controller, depend on the PRESENTATION of your view controller in order to work. If you decide to move views around later, you have to change code that's outside of your view controller. That's bad.)
It's also the reason your code isn't working.
Instead you should set up properties in your destination view controller and set those. Then in your destination view controller's viewDidLoad (only invoked once) or in viewWillAppear (called every time the VC is about to be shown) install the values from your properties into your views as appropriate.
At the time prepareForSegue is called the destination view controller's views haven't been loaded yet. If you look at them you'll find that they are nil. That's why your code isn't working.
Try this test:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"CreateUser"])
{
CreateUserViewController *createUserVC = segue.destinationViewController;
NSLog(#"createUserVC.email = %#", createUserVC.email);
NSLog(#"createUserVC.age = %#", createUserVC.age);
NSLog(#"createUserVC.name = %#", createUserVC.name);
}
}
You will see that all of those outlets are nil.
I have been trying to have a better understanding on how to pass data between view controllers and everything is make more sense but there is one thing I would like to understand better.
I came across this thread/tutorial here at StackOverFlow (Passing Data between View Controllers), I tried it and it worked as expected, but the one thing I don't know understand is why we can change a BOOL property located in the second view controller but NOT a Label. In other words if I add a second property and try to change the label in the prepareForSegue: method it doesn't work, why?
In section Passing Data Forward using Segue's I tried adding a second property for a label in the second view controller, right where isSomethingEnabled is, like this...
#property(nonatomic) BOOL *isSomethingEnabled;
#property (weak, nonatomic) IBOutlet UILabel *myLabel;
Than in prepareForSegue: method located in the first view controller I did this..
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"showDetailSegue"])
{
ViewControllerB *controller = (ViewControllerB *)segue.destinationViewController;
controller.isSomethingEnabled = YES;// this works fine
controller.myLabel.text = #"Hello"; // here, no error but it doesn't update the label
}
}
Why you can modify a BOOL property located in a second view controller but not a label? Can someone explain this a little bit?
Thanks a lot
As Grzegorz Krukowski said the UIViewController isn't loaded at that moment. You can set #property values and access them in the viewDidLoad method and setup your Labels accordingly.
First of all, you should not do this. You should treat another view controller's views as private. The reason for this is the concept of encapsulation. A view controller's views are part of the view's appearance, not it's API. If at a future date you decide to refactor the VC so that it uses the text string in question as the title in a navigation controller, you should be free to do this. If another view controller reaches inside your view controller and manipulates it's view objects directly, you must keep those view objects unchanged forever, or you run the risk of breaking the way the other view controller interacts with it. That's bad, and 6 months from now you won't remember that 3 outside view controllers manipulate your view controller's views, and when you forget and change your views and decide to move a piece of text to a different UI object, your app will stop working correctly.
What you SHOULD do is to add a string property to your ViewControllerB, and set THAT in your prepareForSegue. Then in ViewControllerB's viewWillAppear:animated method, fetch the contents of that string property and install it in your label's text property.
Then the string property becomes part of your view controller's API contract (its public interface.) By adding a public string property to your interface, you guarantee that you will accept string values to that property and that you will do the right thing with them.
Now, as to why it doesn't work. A view controller's view hierarchy is not loaded until it's displayed. At the time of prepareForSegue, the view controller has been initialized, but it's views haven't been loaded yet. All its IBOutlets are nil. (this is another reason why you should not try to manipulate another view controller's views BTW)
Your prepareForSegue method looks like this: (wrong)
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"showDetailSegue"])
{
ViewControllerB *controller = (ViewControllerB *)segue.destinationViewController;
controller.isSomethingEnabled = YES;// this works fine
controller.myLabel.text = #"Hello"; // here, no error but it doesn't update the label
}
}
Instead, your code should look like this:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"showDetailSegue"])
{
ViewControllerB *controller = (ViewControllerB *)segue.destinationViewController;
controller.isSomethingEnabled = YES;// this works fine
controller.textToShow = #"Hello"; //This is the change
}
}
Then, in your ViewControllerB's viewWillAppear:animated method:
- (void) viewWillAppear: animated;
{
[super viewWillAppear: animated];
//install the text from our textToShow property into the label
self.myLabel.text = self.textToShow;
}
Is there anybody who can help a beginner?
I've got two storyboards, the first has a button and the second has a label. I want to push the button and I want the label to display the text.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
second *myVC = segue.destinationViewController;
myVC.mylabel.text = #"Why isn't this working!!";
}
The button acts as the segue.
I would also like to add that I've checked everything and I'm puzzled as to know why it's not working. I would have expect it to work, so if someone could also explain what I'm doing wrong or missing I'd be very grateful.
Thank you for your help.
In prepareForSegue: your second view does not exist yet. The output of mylabel is not connected with the view. Create a new NSString property in your second controller. Set it in prepareForSegue: and use it to setup the label in viewDidLoad: of the second controller.
I have a simple Single View iOS 5 application that I added a second view controller to. I embedded these inside a Navigation Controller and I placed two buttons on my FirstViewController. Each of these buttons have a named identifier for the segue that in turn displays my SecondViewController in the UI of the iPhone application.
All works as expected, BUT, I would like to display in a label on the second view controller something as simple as 'You clicked Button 1'. I have tried placing this data manually in a NSMutableString variable that I declare in my AppDelegate, which I am able to reach in my second view controller but the value I assign to this is never displayed on the screen.
Apparently, a new instance of the SecondViewController is created and this might be why I am not seeing it. I have created an IBOutlet of type UILabel with a name, myLabel, to hold this value but alas, I see no change.
Any help would be greatly appreciated.
NOTE: I am happy to post code but I don't think it will really help with my question.
My solution in this situation is always a custom -init method. Such as,
-initWithButtonPressedString:(NSString*)message;
Declare that in the second view controller and call:
self.secondView = [[SecondViewController alloc]initWithButtonPressedString:#"some conditional string"];
Then, all that's required is an iVar in the second view controller to handle that passed string.
To pass data between view controllers you should consider
1. making a custom init for your view controller which passes in the additional information or
2. creating properties on your second view controller which you access from the first view controller.
You can create a property to the IBOutlet that you made but you need to make sure that if you access it from your first view controller that it is after the views are loaded.
It is hard to give you more direction without seeing your current code
Do you use Storyboards?
Anyways, have you connected IBOutlet to your variable in Interface Builder?
If you use Storyboards, you might try to use method called
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
ie.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"ChooseToDetailsSegue"]) {
preparedSegue = segue;
SaleDetailsViewController *saleDetailsVC = [preparedSegue destinationViewController];
saleDetailsVC.saleDetailsProduct = [elementsArray objectAtIndex:selectedRow];
saleDetailsVC.cardTransactionDetails = [[cardDetails alloc] init];
saleDetailsVC.cardTransactionDetails.number = infoCardNumber;
saleDetailsVC.cardTransactionDetails.month = infoExpiriationM;
saleDetailsVC.cardTransactionDetails.year = infoExpiriationY;
}
}
You just need to init new ViewController (your destination) and refer to their variables.
You can also use notifications. In the IBAction of the first button of your FirstViewController, you can add
NSNotification *messageFromFirstViewController = [NSNotification notificationWithName:#"firstbuttonMessage" object:#"You clicked Button 1"];
[[NSNotificationCenter defaultCenter] postNotification:messageFromFirstViewController];
and something similar for the IBAction of your second button.
In the viewDidLoad of your SecondViewController, you can then add
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(useNotification:) name:#"firstButtonMessage" object:nil];
Create the useNotification method to change your myLabel:
-(void) useNotification:(NSNotification *)notification {
self.myLabel.text = notification.object;}
My problem seems like a generic problem, yet can't seem to find an answer for it.
I have a situation where when the user taps on a custom UITableViewCell, I would like to display an alert and then based on the response to the alert, either stay on the same view (user selecting cancel) or display another view (if the user selects proceed). And I would like to do this using the storyboard feature & segues.
How would one go about this? Do you have to do this the old fashioned way?
#user, Just create the alertView the old fashion way; I do know of any storyboard feature to do this differently. Where storyboard can help is with the segues. You can call the segues programmatically. With you alert view cancel button you can just return (i.e. do nothing). For the other option, to display another view, you can programmatically call a segue to transition to the desired view. If you don't have the proper segue already defined for some other reason on your storyboard, just create a button out and use that to create the segue and name it. Name the segue by clicking on it in storyboard and use the attributes inspector to give it name (identifier). Then hide the button or put it out of the view. I typically put these type of button on the toolbar and use spacers to keep them out of the view. Here's some sample code:
Call the segue from the alert view delegate like this:
[self performSegueWithIdentifier: #"done" sender: self];
Also implement this method to do any necessary task to prepare for the segue:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"done"])
{
// [[segue destinationViewController] setManagedObjectContext:self.managedObjectContext];
// [[segue destinationViewController] setSelectedClient:selectedClient];
}
}
You can create segues directly from the startingViewController to multiple destinationViewControllers that can then be "performed" programmatically. You do not need to create any hidden buttons for them, which does seem like a hack.
OK I came up with a solution in keeping with the storyboard that I like.
Example:
My tableview has 2 sections, grouped, and cells are dynamic prototype. Section 0 contains one row/UITableViewCell & I don't want it to segue. Section 1 contains multiple cells that I want to trigger the segue & drill down into the detail.
In Storyboard:
I removed the segue linking the tableviewcell to the destination view controller.
I made a 'generic' segue linking the source view controller directly to the destination view controller.
In the attributes on the segue, I set the identifier ('EditTimePeriod') and set the type to Push (I presume Modal would work just the same).
In the source view controller:
In the prepareForSegue method I handled both the common 'AddTimePeriod' segue I control-dragged from my UIBarButtonItem (Add), along with the 'generic'(vc-->vc) 'EditTimePeriod' segue.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// handle the click of the 'Add' bar button item
if([segue.identifier isEqualToString:#"AddTimePeriod"]) {
TimePeriodViewController* tpvc = (TimePeriodViewController*)segue.destinationViewController;
tpvc.delegate = self;
// database & entity stuff for adding the new one to the mOC, etc
}
// handle the click of one of the 'editable' cells -
if([segue.identifier isEqualToString:#"EditTimePeriod"]) {
TimePeriodViewController* tpvc = (TimePeriodViewController*)segue.destinationViewController;
tpvc.delegate = self;
TimePeriod * newTP = [self.timePeriodArray objectAtIndex:self.tableView.indexPathForSelectedRow.row];
tpvc.timePeriod = newTP;
}
}
Then I implemented the tableView:didSelectRowAtIndexPath method, and put my condition in here. If the selected row was outside of section zero I called the EditTimePeriod segue manually, defining the sender as the selected tableviewcell:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if(self.tableView.indexPathForSelectedRow.section!=0){
[self performSegueWithIdentifier:#"EditTimePeriod" sender:[tableView cellForRowAtIndexPath:indexPath]];
}
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
return;
}
would be nice to code the cell in section 0 so that it is not selectable in the first place!
Hope this helps though.
** and then 5 minutes later I took another look and realized I could just move the data from section 0 into the section header, which is more intuitive and wasn't being used anyway. leaving the design open for a standard segue from each tableviewcell without needing any condition/check. Was a good exercise anyway though :)