I want to set an NSString property of a main view from a nested view. I do so right now by allocating the previous class and accessing the class.property. When I pop the view controller programmatically and NSLog the property from the main view, it's null.
How does this happen?
EDIT:
MainViewController *controller = [[MainViewController alloc] init];
switch (indexPath.row) {
case 0:
controller.category = #"Categorie 1";
break;
default:
break;
}
[controller release];
You mentioned you are going to pop the viewController so I assume you are trying to set a property of the controller below the navigation stack.
Instead of creating a new object of the class, you should get back the original object that was already created.
NSArray *viewControllers = [self.navigationController viewControllers]; // array of viewControllers currently on the navigation stack.
MainVC *mainVC = (mainVC *)[viewControllers objectAtIndex:viewControllers.count - 2];
[mainVC setProperty:...];
Well first of all, you cannot access a specific instance's properties by simply calling the class. The class has no connection to any specific instance of it.
You need to have an instance variable in your nested view that references the parent. set this up when you create it. Then when you are in the child view controller you can still access the parent.
Something like this in the child:
MyParentViewController *parentVC;
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.
Hi to all,
I passed a variable from first.m to seViewController.m. I'm able to print that variable using NSLog(#variable) but I'm unable to use textField.text=variable. How to print that variable in a textbox?
First.m
-(void)buttonPressed01
{
seViewController *seView = [[seViewController alloc] init];
[seView insert:myString];
[self.navigationController popViewControllerAnimated:YES];
}
seviewcontroller.m
-(void)insert:(NSString*) myString
{
NSLog(#"%#",myString);
textField.text=[NSString stringWithFormat:#"%#",myString];
}
seViewController *seView = [[[seViewController alloc] initWithNibName:#"seViewController" bundle:nil] autorelease];
[seView insert:String];
[[self navigationController] pushViewController:seView animated:YES];
seviewcontroller.m
-(void)insert:(NSString*) myString
{
NSLog(#"%#",myString);
textField.text=myString;
}
Until your seViewController is loaded you can not use its outlets, here you are using textField. It won't set text because after seViewController *seView = [[seViewController alloc] init]; the view is initialized but still you need to load its view in memory in order to use its outlet. so if you are using its view then after addingSubview, or presenting modalView or, pushing it on navigation stack call that method.
or else you should have a string in seviewcontroller which you will set and when controller's will be loaded in memory viewDidLoad just do what you are doing right now in insert method, set TextField's text.
Also, I am not sure what you want to do with seView? why you are creating new instance and not using it ? May be you have created this controller earlier and you want to change that controller's textField text not the new one. Then in that case you should refer that controller not this one because its totally different object.
Thanks
When you pop from stack it loads the instance of the view controller you pushed in to stack. Here you created a new instance and set the text field and loads the instance in the stack. Thats why text field does not show the value to set. If you want to pass the value to the class you nee to pass the value using some singleton class or app delegate.
I think its not working because you are passing this variable to a new instance of your seViewController while you are popping it in the next line. You have to get the same instance as -
seViewController *seView = (seViewController*)[self parentViewController];
[seView insert:myString];
[self.navigationController popViewControllerAnimated:YES];
You can use this one if what I understood is correct,
you can retrieve the array of viewControllers from the navigation controller.
You dont need to initialize the view controller and this wont work as explained above.
How is a ViewController object pushed into view via the navigationController if the object is created in another class than the one which should push the view.
The problem I had is in the class where I created the object and set its values, I had no access to the main navigationController. when I called [self.NavigationController .... It is undefined.
How is this navgation controller accessed? or how can I create a viewController object in one class and set values/push the object into view via another class?
Suppose classA- mainViewController
and classB- userDetailVC
pass the Object to classB before its push on Stack
-(IBAction)buttonClick:(id)sender{
UserDetailVC* userDetailVC=[[UserDetailVC alloc] initWithNibName:#"UserDetailVC" bundle:nil];
[userDetailVC autorelease];
//imp- set the object over here
NSDictionary* tempDict=[[NSDictionary alloc] initWithDictionary:self.userInfoDict];
userDetailVC.userInfoDict=tempDict;
[self.navigationController pushViewController:userDetailVC animated:YES];
}
I want to push a view controller onto the stack, then pop the first one that pushed the new one.
-(void) someMethod {
MegaSuperAwesomeViewController *tempVC = [[MegaSuperAwesomeViewController alloc] init];
[self.navigationController pushViewController:tempVC animated:YES];
[tempVC release];
// pop this VC, how?
}
EDIT: turns out I can pop back 2 view controllers instead once finished with the new VC. Still not what I wanted exactly, but it works. The downside is I need to set a flag to indicate that the covered view is completed.
Here's a technique of popping back two view controllers, which has a similar problem of yours of the current view controller and its navigationController property going away as soon as you do the first pop:
// pop back 2 controllers on the stack to the setup screen
//
// locally store the navigation controller since
// self.navigationController will be nil once we are popped
//
UINavigationController *navController = self.navigationController;
// retain ourselves so that the controller will still exist once it's popped off
//
[[self retain] autorelease];
// Pop back 2 controllers to the setup screen
//
[navController popViewControllerAnimated:NO];
[navController popViewControllerAnimated:YES];
alternatively, you can directly "party" on the navigation controllers stack of view controllers:
setViewControllers:animated: Replaces
the view controllers currently managed
by the navigation controller with the
specified items.
(void)setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated Parameters
viewControllers The view controllers
to place in the stack. The
front-to-back order of the controllers
in this array represents the new
bottom-to-top order of the controllers
in the navigation stack. Thus, the
last item added to the array becomes
the top item of the navigation stack.
animated If YES, animate the pushing
or popping of the top view controller.
If NO, replace the view controllers
without any animations. Discussion You
can use this method to update or
replace the current view controller
stack without pushing or popping each
controller explicitly. In addition,
this method lets you update the set of
controllers without animating the
changes, which might be appropriate at
launch time when you want to return
the navigation controller to a
previous state.
If animations are enabled, this method
decides which type of transition to
perform based on whether the last item
in the items array is already in the
navigation stack. If the view
controller is currently in the stack,
but is not the topmost item, this
method uses a pop transition; if it is
the topmost item, no transition is
performed. If the view controller is
not on the stack, this method uses a
push transition. Only one transition
is performed, but when that transition
finishes, the entire contents of the
stack are replaced with the new view
controllers. For example, if
controllers A, B, and C are on the
stack and you set controllers D, A,
and B, this method uses a pop
transition and the resulting stack
contains the controllers D, A, and B.
Availability Available in iOS 3.0 and
later. Declared In
UINavigationController.h
So, to "disappear" the view controller directly under you on the navigation stack, in your view controller's viewDidLoad, you could do this:
NSMutableArray *VCs = [self.navigationController.viewControllers mutableCopy];
[VCs removeObjectAtIndex:[VCs count] - 2];
self.navigationController.viewControllers = VCs;
I had trouble figuring this out also so I wanted to share how I got this to work.
Let's say you have a stack of VCs VC1 being the root then you push VC2 and from VC2 you want to push VC3 but once pushed you don't want the user to go back to VC2 but rather to VC1 (the root). The way to do that is:
//push VC3 from VC2
[[self navigationController] pushViewController:VC3 animated:YES];
// now remove VC2 from the view controllers array so we will jump straight back to VC1
NSMutableArray *viewHeirarchy =[[NSMutableArray alloc] initWithArray:[self.navigationController viewControllers]];
[viewHeirarchy removeObject:self];
self.navigationController.viewControllers = viewHeirarchy;
Hope this helps someone else
Thanks Bogatyr about the tip on 'party on the viewcontroller array for the navcontroller'. I just replaced the entire stack with the one viewcontroller I want to change to, and then log out all the viewcontrollers in the stack to make sure its the only one! Worked great - thanks!
RatingsTableViewController *newViewController = [[RatingsTableViewController alloc] init];
NSMutableArray * newVCarray = [NSMutableArray arrayWithObjects:newViewController, nil];
self.navigationController.viewControllers = newVCarray;
[newViewController release];
NSMutableArray *allControllers = [[NSMutableArray alloc] initWithArray:self.navigationController.viewControllers];
for (id object in allControllers) {
NSLog(#"name VC: %#", object);
}
[allControllers release];
-(void)popToSelf{
NSArray *array = [self.navigationController viewControllers];
for (int i = 0 ; i < array.count ; i++) {
UIViewController *currentVC = [array objectAtIndex:i];
if ([currentVC isKindOfClass:[YourViewControllerClass class]]) {
[self.navigationController popToViewController:[array objectAtIndex:i] animated:YES];
}
}
}