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.
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];
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Passing Data between View Controllers
I have two view controllers and I want to get some information from the previous view in my app. e.g:
In my app I go from the first page to the second. Depending on what button the user pushes, I want to change the info on the second screen. What's the fastest and easiest way to do this? I tried importing the class and rebuilding but that recreates the string obj and doesn't keep the info that I want.
Well there are (at least) two possibilities:
Add a property to the next view controller, do something like
NewVC *vc = [[NewVC alloc] init]; // or initWithNibName...
[vc setMyInformation:information];
Create a custom init method:
NewVC *vc = [[NewVC alloc] initWithMyInformation:information andNibName:#"nibName" bundle:nil]; // well you should get the point...
In your second ("child") view controller, keep a property for the string (see section 9).
When you instantiate the second view controller and before you push it onto the stack from the first view controller, set the string property's value, e.g., retain the first controller's string:
mySecondViewController.infoString = myFirstViewController.infoString;
Make sure your second view controller manages the memory for the string (usually with a release message in the controller's dealloc method, assuming your property is defined with a retain).
A second option is to keep properties in your application delegate, or another singleton that manages data for the application. But the first approach is a bit more lightweight.
If I understand you correctly, what you need is to create an instance variable in vc2. And then when you create an instance of vc2 from vc1 you can access that iVar to assign value, etc. before you present vc2. Here is an example:
In ViewController2.h file:
#interface ViewController2
{
NSString *string2; //create an instance variable
}
#property (nonatomic, retain) NSString *string2;
In ViewController2.m file:
#implementation ViewController2
#synthesize string2;
In ViewController1.m file:
#implementation ViewController1
//ViewController2 *viewController2 = [[ViewController2 alloc] initWithNibName:#"ViewController2" bundle:nil]; //one way to instantiate
viewController2.string2 = #"whatever string"; //here you assign the value to the instance variable string2 in viewController2
//[self.navigationController pushViewController:childController animated:YES]; //etc. it depend on how you present viewcontroller2
Obviously, I'm new to iOS development, but I could really use somebody's help. I am building a tab bar application in which I am attempting to load .plists into drill-down table views. The problem is that I can't seem to get this method right, as I am trying to use the navigation controller for a tab in its ViewController. I'm positive that my error is in the second line.
A37dgAppDelegate *AppDelegate = (A37dgAppDelegate *)[[UIApplication sharedApplication] delegate];
AppDelegate.indNavControl *indNavControl;
Subsequently, I get some errors. Here is the code, and I'll point out where the errors are:
if([Children count] == 0) {
DetailViewController *dvController = [[DetailViewController alloc] initWithNibName:#"DetailView" bundle:[NSBundle mainBundle]];
[self.indNavControl pushViewController:dvController animated:YES];//Property "indNavControl" not found on object of type "IndustriesViewController"
[dvController release];
}
else {
//Prepare to tableview.
IndustriesViewController *indViewControl = [[IndustriesViewController alloc] initWithNibName:#"IndustryView" bundle:[NSBundle mainBundle]];
//Increment the Current View
indViewControl.CurrentLevel += 1;
//Set the title;
indViewControl.CurrentTitle = [dictionary objectForKey:#"Title"];
//Push the new table view on the stack
[self.indNavControl pushViewController:indViewControl animated:YES]; //Property "indNavControl" not found on object of type "IndustriesViewController"
indViewControl.tableDataSource = Children;
[indViewControl release];
}
}
Just to be clear, I have imported the header file of my App Delegate. Any help would be greatly appreciated :)
The line AppDelegate.indNavControl *indNavControl; is wrong. I think you don't need this line at all. And while pushing new view controller you can directly use AppDelegate.indNavControl instead of self.indNavControl,
[AppDelegate.indNavControl pushViewController:...
Your theory that the problem is in the second line is correct.
AppDelegate.indNavControl *indNavControl;
that line isn't really doing anything. You need a class where AppDelegate.indNavControl is.
You have a couple of options:
1) if you want indViewControl to be a 'public' property
* in your IndustriesViewController.h, move the declaration of indViewControl there.
#interface
{
WhateverClass *indNavControl;
}
#property (retain) WhateverClass*indNavControl;
#end
2) if you want a private property then add an empty category at the top of your .m with the ivar and property declaration from above.
What the compiler is complaining about is trying to access a property that doesn't exist.
calling self.whateverIvar requires an #property definition.
first create the instance of the appdelegate like and then you can access, whatever variables you have declared in appdelegate as property,synthesize.There is no need to create seperate variable of indNavControl.
When should I use init: and when should I use initWithNibName:bundle: when creating a view controller?
-initWithNibName:bundle: is the designated initializer for UIViewController. Something should eventually call it. That said, and despite Apple's examples (which favor brevity over maintainability in many cases), it should never be called from outside the view controller itself.
You will often see code like this:
MYViewController *vc = [[MYViewController alloc] initWithNibName:#"Myview" bundle:nil];
I say this is incorrect. It puts implementation details (the name of the NIB and the fact that a NIB is even used) into the caller. That breaks encapsulation. The correct way to do this is:
MYViewController *vc = [[MYViewController alloc] init];
Then, in MYViewController:
- (instancetype)init
{
self = [super initWithNibName:#"Myview" bundle:nil];
if (self != nil)
{
// Further initialization if needed
}
return self;
}
- (instancetype)initWithNibName:(NSString *)nibName bundle:(NSBundle *)bundle
{
NSAssert(NO, #"Initialize with -init");
return nil;
}
This moves the key implementation details back into the object, and prevents callers from accidentally breaking encapsulation. Now if you change the name of the NIB, or move to programmatic construction, you fix it in one place (in the view controller) rather than in every place the view controller is used.
Use initWithNibName: bundle: if you are... initializing with a nib file! That is, a file that you made using Interface Builder.
If you aren't using IB to layout your views, you can just use init.
You can just call init, as long as the xib has the same name as the view controller class. The encapsulation is not necessary. This saves typing, but may not serve clarity.
NUDMainViewController *mainVC = [[NUDMainViewController alloc] init];
using init when there is no nib/xib file, e.g. UI are created by coding
using initWithNibName , if we have an nib/xib or same controller share by more than 1 nib/xib
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
self.viewController = [[ViewController alloc] initWithNibName:#"ViewController_iPhone" bundle:nil];
} else {
self.viewController = [[ViewController alloc] initWithNibName:#"ViewController_iPad" bundle:nil];
}
that's what I think..
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).