I have an iPhone app with a tableviewcontroller. When you click a certain cell it opens a new uiviewcontroller with this code:
nextViewController = [[avTouchViewController alloc] initWithNibName:#"avTouchViewController" bundle:nil];
The uiviewcontroller above called avTouchViewController has a property that looks like:
IBOutlet SomeObject *controller;
SomeObject is an object with all relevant view properties.
I would like to pass an nsstring parameter from the tableviewcontroller I initialize the avTouchViewController with to someObject.
How can I do this?
I'm a little confused by your question; you say you're creating your avTouchViewControllers when a cell is tapped inside an existing UITableView, but your last part describes the inverse situation.
Basically, if you want to pass information to a view controller, just give it a property that can be set (which may already be the case), e.g.:
nextViewController = [[avTouchViewController alloc] initWithNibName:#"avTouchViewController" bundle:nil];
nextViewController.controller = theInstanceOfSomeObjectIWantToPass;
You also may want to rename your controller property. To a reader, it doesn't make sense that a view controller has a property called controller which is actually a SomeObject*. As well, your class names should be capitalized, i.e. use AvTouchViewController instead of avTouchViewController.
If I were doing this I would add my own initializer to my UIViewController subclass:
- (id)initWithController:(NSString *pController) {
if (self = [super initWithNibName:#"avTouchViewController" bundle:nil]) {
self.controller = pController;
}
return self;
}
And then just call the following (in tableView:didSelectRowAtIndexPath: or whereever):
NSString *controller = #"Sample String";
AVTouchViewController *nextViewController = [[AVTouchViewController alloc] initWithController:controller];
[controller release];
[self.navigationController pushModalViewController:nextViewController animated:YES];
[nextViewController release];
As a point of style, class names conventionally begin with uppercase letters (hence my change to AVTouchViewController).
Related
I have an app in which I am loading variable view controllers depending on where the user is in the app. This is my code.
-(IBAction)buttonPressed:(id)sender;{
if (mission <1) {
gameViewController *detailViewController = [[gameViewController alloc] initWithNibName:#"gameViewController" bundle:nil];
[self.navigationController pushViewController:detailViewController animated:YES];
detailViewController.which2 = which;
}
else if (mission > 0) {
NSString *viewController = #"gameViewController";
NSString *missionViewController = [viewController stringByAppendingString:missionNo];
Class controllerClass = NSClassFromString (missionViewController);
id detailViewController = [[controllerClass alloc] initWithNibName:#"gameViewController" bundle:nil];
NSLog(#"missionViewController;%#",missionViewController);
[self.navigationController pushViewController:detailViewController animated:YES];
detailViewController .which2 = which;
}
}
everything work great except I want to pass a string from the first view controller to the second view controller which ever one that may be.
As you can see I have put in the code just above detailViewController.which2 = which;
I have created the property and synthesized NSString *which in my first view controller and NSString *which2 in all the subsequent view controllers. in the first instance where mission is <1 everything works ok and NSLog shows the string being passed. However with the second detailViewController (which is the variable view controller) I get the error Property 'which2' not found on object of type"_strong id' Does anyone have any suggestion on how to resolve this?
the other viewControllers are gameViewController1, gameViewController2, etc. Each is rather long and complex. But they all load into the same xib file gameViewController. There is a UIlabel that update to one higher once the user finishes that gameView so they can go on the the next on in the series or go back to the main menu. If they go back to the main menu the number is added to "gameViewController" so the correct one is loaded. So I can't specify which view controller is going to load since it depends on the user's place. Thus the missionViewController with the # of mission added to load the correct view controller. Each of the subsequent view controllers has a which2 created and synthesized. What if we pretend that all subsequent view controllers just had a UILabel that is going to display the string "which2 in it. All I want to do is pass the string "which" to the next viewController (whichever one that is) as "which2".
Dynamic binding allows you to send messages to an id as long as the selector exists in the project, but dot-syntax is not allowed.
Changing
detailViewController.which2 = which;
to
[detailViewController setWhich2:which];
should suppress the warning.
write this : `detailViewController .which2 = which;
just before you push navigation controller.`
UPDATE:
Use Protools to update the value.
#protocol MissionProtocol
#required
-(void) updateValue:(NSSTring*) value;
#end
Implement the protocols in your ViewControllers. i.e.
#interface MissionViewController:UIViewController<MissionProtocol>
....
#end
In your implementation file, implement the method updateValue.
-(void) updateValue:(NSString*) value
{
self.which2=value;
}
Then change your original code to:
NSString *viewController = #"gameViewController";
NSString *missionViewController = [viewController stringByAppendingString:missionNo];
Class controllerClass = NSClassFromString (missionViewController);
id<MissionProtocol> detailViewController = [[controllerClass alloc] initWithNibName:#"gameViewController" bundle:nil];
[detailViewController updateValue:which];
I have a RootViewController that calls an AddQuoteViewController and there is a variable "subject_id" that I set in the RootViewController that does not show up in AddQuoteViewController and I need help understanding why and how to fix it.
Subject *sub = [self.tableDataSource objectAtIndex:indexPath.row];
self.subject_id = sub.subject_id;
[self addQuote_Clicked: sub];
...
- (void) addQuote_Clicked:(id)sender {
if(aqvController == nil)
aqvController = [[AddQuoteViewController alloc] initWithNibName:#"AddQuoteView" bundle:nil];
if(addNavigationController == nil)
addNavigationController = [[UINavigationController alloc] initWithRootViewController:aqvController];
[self.navigationController presentModalViewController:addNavigationController animated:YES];
}
Then in my AddQuoteViewController I try to access this variable like this:
RootViewController *rv = [RootViewController alloc] ;
NSLog(#"rv.Subject_id = %d", rv.subject_id);
But get nothing. There must be a simple way to do this.
First of all, there's an error in your third block of code. This:
RootViewController *rv = [RootViewController alloc] ;
Should be this:
RootViewController *rv = [[RootViewController alloc]init];
But strictly speaking that's not why you aren't seeing your instance variable.
If I understand correctly, the first two blocks of code are in RootViewController, and they instantiate an AddQuoteViewController and present it. Then, from your third block of code, which is in AddQuoteViewController, you want to access a member variable (subject_id) of the RootViewController that brought it up.
The approach of instantiating a RootViewController from within the AddQuoteViewController wouldn't work, because you're creating a different instance of RootViewController. What you're after is the value in the instance you just came from.
Perhaps the easiest way to do it is to create a corresponding property on AddQuoteViewController and set it when it's created:
- (void) addQuote_Clicked:(id)sender {
if(aqvController == nil)
aqvController = [[AddQuoteViewController alloc] initWithNibName:#"AddQuoteView" bundle:nil];
if(addNavigationController == nil)
addNavigationController = [[UINavigationController alloc] initWithRootViewController:aqvController];
aqvController.subject_id = self.subject_id;
[self.navigationController presentModalViewController:addNavigationController animated:YES];
}
You'll need to create the subject_id property on AddQuoteViewController the same way you did on RootViewController.
There are various ways of doing this, but a short answer - You can set a reference to the RootViewController as a property on your AddQuoteViewController
i.e.
in AddQuoteViewController.h
RootViewController *rvc
...
#property (nonatomic,assign) RootViewController *rvc;
and corresponding synthesize and release in your implementation class. (AddQuoteViewController.m)
Then when you create your AddQuoteViewController inside your RootViewController, also set this property:
- (void) addQuote_Clicked:(id)sender {
if(aqvController == nil)
aqvController = [[AddQuoteViewController alloc] initWithNibName:#"AddQuoteView" bundle:nil];
aqvController.rvc = self;
... etc.
Then you can access any property of the root view controller inside your AddQuoteViewController via this property:
NSLog(#"rv.Subject_id = %d", self.rv.subject_id);
As a side note there are a few things you are doing in your question that are a bit unusual, like trying to get a reference to an object by allocating a new one, and also creating a new navigation controller and presenting it as a modal view controller typically you would do one or the other (and wouldn't need to create a new navigation controller). i.e. you would either create a view controller and present it modally, or you would create a view controller and push it onto your current navigation controller stack.
You can't access variables across view controllers like that.
Have a look at creating a singleton class which will be globally accessible.
One exception I think is you can access them in the AppDelegate, but it's not advisable to have global vars.
I am trying to pass a string back and forth between the view Controllers, so for example as soon as I click on a tab bar button (+) in the first View, second view opens (PresentModalViewController) and it has a Text Field. So anything I type, I take it into a string(this string is an object of the first view) and I am trying to append that string to a tableview loaded in the first View.
Note: My string object is declared like this
View1.h
NSString *string
#property (copy) NSString *string;
View1.m
#synthesize string;
And in the View 2 I am passing the textField Value like this
View2.m
View1 *view1 = [[View1 alloc] initWithNibName:#"View1" bundle:nil];
view1.string = [[NSString alloc] initWithFormat:#"%#", TextField.text];
Problem - When I NSLog this value inside the View2, it grabs the value from the Text Field but in order for me to load the previous view, I need to dismiss this View2. So as soon as this View2 is dismissed when I try to access the same string object in my view 1. It says the string object is null.
Question - Could someone tell me
1. How to get the text Field value from view 2 to view 1 after dismissing View 2 (does it really makes all its objects null when dismissed?)
2. How to append that string to the last index of a NSMutableArray?
This is a very good question that I also had trouble figuring out when I started coding for the iOS. Basically, you don't need to initialize a new view1 because the tabbar controller already holds the view1 object in its viewControllers property. Also, alloc/init'ing the string in not necessary in this situation.
Therefore, you would want to change this:
View1 *view1 = [[View1 alloc] initWithNibName:#"View1" bundle:nil];
view1.string = [[NSString alloc] initWithFormat:#"%#", TextField.text];
To something like this:
View1 *view1 = [self.tabbarController.viewControllers objectAtIndex:0];
view1.string = textField.text;
Or even:
((View1 *)[self.tabbarController.viewControllers objectAtIndex:0]).string = textField.text;
Part 2:
How to append that string to the last index of a NSMutableArray?
NSMutableArray *someArray = [[NSMutableArray alloc] init];
[someArray addObject:string];
[someArray addObject:#"anotherString"];
The answer from #chown will definitely work if the ViewController you're sending the string to is the base controller of a tabBarController.
If you were several levels deep into a NavigationController stack, then you'll need a different approach.
The approach I'd recommend would be to create a protocol. This is where you create a delegate of view2 to pass the string back down the stack before the view is dismissed.
There are loads of examples of this code both in the Apple Documentation and on the Internet (StackOverflow included) but here's a quick run down...
In View2.h
#import <UIKit/UIKit.h>
//define the protocol, so you can set the delegate to this type
#protocol View2Delegate;
#interface View2 : UIViewController
//other properties etc
#property (assign) id <View2Delegate> delegate;
#end
//put the actual protocol definition here so we can pass a reference to ourself back up too if needed...
#protocol View2Delegate
- (void)view2:(View2*)view passingStringBack:(NSString *)stringToPassBack;
#end
In View2.m you can call that delegate method where ever you like but here's an example:
- (void)viewWillDisappear:(BOOL)animated
{
if(self.delegate)
[self.delegate view2:self passingStringBack:#"String I'm passing back"];
[super viewWillDisappear:animated];
}
Then in View1.h
#interface View2 : UIViewController <View2Delegate>
and View1.m
- (void)view2:(View2*)view passingStringBack:(NSString *)stringToPassBack
{
NSLog(#"%#", stringToPassBack);
}
Another option would be to post a notification, but that is more a broadcast scenario than a targeted message so I won't bother posting example code for that.
I have two view controllers, firstViewController and secondViewController. I am using this code to switch to my secondViewController (I am also passing a string to it):
secondViewController *second = [[secondViewController alloc] initWithNibName:nil bundle:nil];
second.myString = #"This text is passed from firstViewController!";
second.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:second animated:YES];
[second release];
I then use this code in secondViewController to switch back to the firstViewController:
[self dismissModalViewControllerAnimated:YES];
All of this works fine. My question is, how would I pass data to the firstViewController? I would like to pass a different string into the firstViewController from the secondViewController.
You need to use delegate protocols... Here's how to do it:
Declare a protocol in your secondViewController's header file. It should look like this:
#import <UIKit/UIKit.h>
#protocol SecondDelegate <NSObject>
-(void)secondViewControllerDismissed:(NSString *)stringForFirst
#end
#interface SecondViewController : UIViewController
{
id myDelegate;
}
#property (nonatomic, assign) id<SecondDelegate> myDelegate;
Don't forget to synthesize the myDelegate in your implementation (SecondViewController.m) file:
#synthesize myDelegate;
In your FirstViewController's header file subscribe to the SecondDelegate protocol by doing this:
#import "SecondViewController.h"
#interface FirstViewController:UIViewController <SecondDelegate>
Now when you instantiate SecondViewController in FirstViewController you should do the following:
// If you're using a view controller built with Interface Builder.
SecondViewController *second = [[SecondViewController alloc] initWithNibName:"SecondViewController" bundle:[NSBundle mainBundle]];
// If you're using a view controller built programmatically.
SecondViewController *second = [SecondViewController new]; // Convenience initializer that uses alloc] init]
second.myString = #"This text is passed from firstViewController!";
second.myDelegate = self;
second.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:second animated:YES];
[second release];
Lastly, in the implementation file for your first view controller (FirstViewController.m) implement the SecondDelegate's method for secondViewControllerDismissed:
- (void)secondViewControllerDismissed:(NSString *)stringForFirst
{
NSString *thisIsTheDesiredString = stringForFirst; //And there you have it.....
}
Now when you're about to dismiss the second view controller you want to invoke the method implemented in the first view controller. This part is simple. All you do is, in your second view controller, add some code before the dismiss code:
if([self.myDelegate respondsToSelector:#selector(secondViewControllerDismissed:)])
{
[self.myDelegate secondViewControllerDismissed:#"THIS IS THE STRING TO SEND!!!"];
}
[self dismissModalViewControllerAnimated:YES];
Delegate protocols are EXTREMELY, EXTREMELY, EXTREMELY useful. It would do you good to familiarize yourself with them :)
NSNotifications are another way to do this, but as a best practice, I prefer using it when I want to communicate across multiple viewControllers or objects. Here's an answer I posted earlier if you're curious about using NSNotifications: Firing events accross multiple viewcontrollers from a thread in the appdelegate
EDIT:
If you want to pass multiple arguments, the code before dismiss looks like this:
if([self.myDelegate respondsToSelector:#selector(secondViewControllerDismissed:argument2:argument3:)])
{
[self.myDelegate secondViewControllerDismissed:#"THIS IS THE STRING TO SEND!!!" argument2:someObject argument3:anotherObject];
}
[self dismissModalViewControllerAnimated:YES];
This means that your SecondDelegate method implementation inside your firstViewController will now look like:
- (void) secondViewControllerDismissed:(NSString*)stringForFirst argument2:(NSObject*)inObject1 argument3:(NSObject*)inObject2
{
NSString thisIsTheDesiredString = stringForFirst;
NSObject desiredObject1 = inObject1;
//....and so on
}
I could be way out of place here, but I am starting to much prefer the block syntax to the very verbose delegate/protocol approach. If you make vc2 from vc1, have a property on vc2 that you can set from vc1 that is a block!
#property (nonatomic, copy) void (^somethingHappenedInVC2)(NSString *response);
Then, when something happens in vc2 that you want to tell vc1 about, just execute the block that you defined in vc1!
self.somethingHappenedInVC2(#"Hello!");
This allows you to send data from vc2 back to vc1. Just like magic. IMO, this is a lot easier/cleaner than protocols. Blocks are awesome and need to be embraced as much as possible.
EDIT - Improved example
Let's say we have a mainVC that we want to present a modalVC on top of temporarily to get some input from a user. In order to present that modalVC from mainVC, we need to alloc/init it inside of mainVC. Pretty basic stuff. Well when we make this modalVC object, we can also set a block property on it that allows us to easily communicate between both vc objects. So let's take the example from above and put the follwing property in the .h file of modalVC:
#property (nonatomic, copy) void (^somethingHappenedInModalVC)(NSString *response);
Then, in our mainVC, after we have alloc/init'd a new modalVC object, you set the block property of modalVC like this:
ModalVC *modalVC = [[ModalVC alloc] init];
modalVC.somethingHappenedInModalVC = ^(NSString *response) {
NSLog(#"Something was selected in the modalVC, and this is what it was:%#", response);
}
So we are just setting the block property, and defining what happens when that block is executed.
Finally, in our modalVC, we could have a tableViewController that is backed by a dataSource array of strings. Once a row selection is made, we could do something like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *selectedString = self.dataSource[indexPath.row];
self.somethingHappenedInModalVC(selectedString);
}
And of course, each time we select a row in modalVC, we are going to get a console output from our NSLog line back in mainVC. Hope that helps!
hmm, look for the notification centre and pass back info in a notification. here is apples take on it
- I take this approach personally unless any one has any other suggestions
Define a delegate protocol in the second view controller and make the first one the delegate of the second.
I'm trying to load a NIB based on a variable I get from my settings file. This is the code:
//select the right nib name
NSString *nibVar = [nibs objectForKey:#"controller"];
// create the view controller from the selected nib name
UIViewController *aController = [[UIViewController alloc] initWithNibName:nibVar bundle:nil];
aController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:aController animated:YES];
[aController release];
This unfortunately does not work.
Any ideas here?
Thanks
You cannot instantiate "UIViewController" with arbitrary NIBs, you have to instantiate "[whatever your custom view controller class is]" with the NIB for that class.
It's crashing because it's trying to access properties that don't exist in UIViewController.
If you want to do this kind of dynamic view-controller loading, you need to do a bit more work, and use the special Class class method that lets you instantiate an object using a string for the class name, instead of hard-coded.
Sometehing like:
Class viewControllerClass = NSClassFromString( nibVar );
UIViewController* aController = (UIViewController*) [[viewControllerClass alloc] initWithNibName:nibVar bundle:nil];
Make sure the NIB name is correct and does not include the xib extension. It is also case sensitive.