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;}
Related
I have 2 view controllers . First one is UIview controller and second one is table view controller.
I want to send data 2nd (table view controller) to first(uiview Controller) after the selection of rows of 2nd view controller.
For this i have written a delegate protocol.
But my delegate protocol is not working...
I figured out the problem.The object of second view controller that i am creating.
address = [[second_viewcontroller alloc] init];
address.delegate = self;
is different from self of second_viewcontroller view controller page.
How to make this two same.
self = [[second_viewcontroller alloc] init];
your problem my delegate protocol is not working... I figured out the problem.The object of second view controller that I am creating. address=[[second_viewcontroller alloc]init]; address.delegate=self; is different from self of second_viewcontroller view controller page.
It's clear say that way you create the second_viewcontroller object is not right.
You have to create the object from ViewController storyboard identifier.
First give the Storyboard ID to ViewController from Storyboard.Follow this step to Giving the Storyboard ID.
Select the particular ViewController in Storyboard.
Go to IdentityInspector.
Under IdentityInspector, There is identity section and add the Storyboard ID In "Storyboard ID" Field.
Syntax For Creating a ViewController Object.
Second_viewController *aVC = [self.storyboard instantiateViewControllerWithIdentifier:#"Second_viewController"];
aVC.delegate = self;
I assume that you are calling the Second_ViewController from storyboard instead of doing programmatically.
In that case, the correct instance of Second_ViewController can be accessed in prepareForSegue. For that, you need to set a Storyboard segue identifier, eg "Second_ViewController"
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"Second_ViewController"]) {
SecondViewController *aSecVC = segue.destinationViewController;
// Register the Delegate to self.So when we call the delegate method from secondVC, SendMessage will be call of ViewController
aSecVC.delegate = self;
}
}
If you use alloc-init or instantiateViewControllerWithIdentifier, when you are using a storyboard push segue, it will create another instance.
Yes as your instantiating a new instance of the second view controller. From what I could make out from your question, I guess if you obtain the instance of your secondViewController from the Navigation Stack it should work
I created a sample project for you to get the basic knowledge of how to pass data backward using NSUserDefaults. try this in GitHub. hope this will help to you. project url Pass data backward using NSUserDefaults in Objective-C
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;
}
I'm working with the Master-Detail project template that comes with Xcode and referenced in http://developer.apple.com/library/ios/#documentation/iPhone/Conceptual/SecondiOSAppTutorial/
Problem: I am trying to figure out how to add additional UIViewControllers to the default UINavigationController that this template comes with.
Specifically, I would like to add a DetailEditViewController after DetailViewController. Here is what I've done to this effect so far:
In DetailViewController I added an edit button to the navigationItem:
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.rightBarButtonItem =
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit
target:self
action:#selector(editDetailItem:)];
[self configureView];
}
You can see it specifies a message selector editDetailItem:, which I've implemented as:
- (void)editDetailItem:(id)sender
{
[self.navigationController pushViewController:
[[DetailEditViewController alloc] init] animated:YES];
}
I've created a DetailEditViewController on the Storyboard, and the code runs without crashing, producing a black, blank window with a navigation item to take me back to detail. From here on I am pretty confused:
When I drag a new View Controller to the Storyboard, no corresponding code files are created! Am I responsible for making code files for these controllers? I see that Storyboard View Controllers are associated with a Class in the Identity Inspector... but why on earth would it not create templates for a new UIViewController when I drag one onto the Storyboard?
Should I be using a seque instead of -pushViewController to get from DetailViewController to DetailEditViewController? If so, I'm not sure how to add one on the Storyboard, because the navigationItem's UIBarButtonItems are all added in-code. There's nothing to Ctrl-drag from.
How do I send information from DetailViewController to DetailEditViewController? When MasterViewController segues to DetailViewController, it specifies the sender via - prepareForSegue:sender:
You're right, no corresponding files are produced. How is the system supposed to know what class you want? You need to create a UIViewController subclass, and change the class of the controller you drag in, to that class. The easiest way to push the new controller is to use a push segue -- if you don't have a UI element in the storyboard to connect that to, you connect it directly from the controller and give the segue an identifier (which I call "GoToEdit" in my example). In the action method for the edit button, then perform the segue:
[self performSegueWithIdentifier:#"GoToEdit" sender:self];
If you want to pass information, then you implement prepareForSegue:, something like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"GoToEdit"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
NSDate *object = _objects[indexPath.row];
[[segue destinationViewController] setDetailItem:object];
}
}
It's a good thing to check the segue identifier first. Then you can access your destinationViewController (you might have to cast it to your class, so the compiler will recognize any property of it you're trying to set), and pass what you want to it.
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.
Here's what I've got:
I have a root view controller with an array of images. I have a detailViewController which simply has a fullscreen UIImageView for displaying a full screen image. I also have a segue defined that presents the detailViewController. This segue is programmatically invoked when the user selects an image in the rootViewController like so:
- (void)carousel:(iCarousel *)carousel didSelectItemAtIndex:(NSInteger)index
{
[self performSegueWithIdentifier:#"fullScreenSegue" sender:self];
}
The problem is that the detailViewController has an image property that it sets as the image for the UIImageView it displays and I need a way to pass the image to it to display. How do I do this? According to apple docs, I should implement the prepareForSegue method in the calling controller, but at that point, I no longer have access to the index above. The images are in an array in the root view controller and I need that index to know which image to pass to the detailViewController. I also set the detailViewController to have a protocol that sets the calling controller as the delegate and then calls a "setImage" method in the init() method, but that doesn't even seem to work and seems like a crazy implementation anyway...
I thought about declaring a property in the rootViewController like #property int selectedIndex, then setting that property in the above method and then in the prepareForSegue method doing something like:
detailViewController.imageForDisplay = [images objectAtIndex:selectedIndex];
That would probably work, but that approach rubs me wrong...
Thanks in advance for the help!
Two ways to handle it. One use prepareForSegue and save your index somewhere before you call the segue...
self.selectedIndex = index;
[self performSegueWithIdentifier:#"fullScreenSegue" sender:self];
then in the prepareForSegue use:
if([[segue identifier] isEqualToString:#"fullScreenSegue"])
{
DetailViewController *detailViewController = segue.destinationViewController;
detailViewController.imageForDisplay = [self.images objectAtIndex:self.selectedIndex];
}
Second way is to use your delegate and protocol, but make the setImage call in your viewDidLoad method, not in your init.
Hope that helps. It seems like you were getting pretty close.