Call a parent view controller (through a navigationcontroller) - iphone

Currently I add a viewcontroller using pushViewController:animated: and now from within that new one would like to call a method inside my "parent"-controller. I think I get stuck at the navigationcontroller.
Currently trying this to see if it's the controller I want:
if([self.superclass isKindOfClass:[MySuperController class]])
// and tried:
if([self.presentingViewController isKindOfClass:[MySuperController class]])
None of these two worked.
How can I access the controller (a method in it) that pushed the current one?

Just like Marsson mentioned, you need to use delegate...
Here is an sample:
In your child view controller .h file:
#protocol ChildViewControllerDelegate <NSObject>
- (void)parentMethodThatChildCanCall;
#end
#interface ChildViewController : UIViewController
{
}
#property (assign) id <ChildViewControllerDelegate> delegate;
In your child view controller .m file:
#implementation ChildViewController
#synthesize delegate;
// to call parent method:
// [self.delegate parentMethodThatChildCanCall];
In parent view controller .h file:
#interface parentViewController <ChildViewControllerDelegate>
In parent view controller .m file:
//after create instant of your ChildViewController
childViewController.delegate = self;
- (void) parentMethodThatChildCanCall
{
//do thing
}

self.navigationController.viewControllers
Returns an array of all the view controllers in the navigation stack. The current view controller is at the top of the stack, the previous view controller is the next one down, and so forth.
So, you can do the following:
NSArray *viewControllers = self.navigationController.viewControllers;
int count = [viewControllers count];
id previousController = [viewControllers objectAtIndex:count - 2];
if ([previousController respondsToSelector:#selector(myMethod)])
[previousController myMethod];
If you know what class the previous controller is you can cast it explicity instead of using id.

Not sure of your application logic, but you can always do this.
In your "child" controller, declare property of type parent-controller. So, in your .h file:
MySuperController *superController;
property(nonatomic, retain)MysuperController *superController;
and in your .m file:
#synthesize superController;
Before "pushing" your child controller:
MyChildController *child = ...
[child setSuperController:self];
[self.navigationController pushViewController:child animated:YES];
Then in your child controller you can simply access your super with
[this.superController myMethod:param];
I'm not going to advocate this way of coding, but it's a quick/cheap/dirty way to accomplish things.

Related

pass data from mainView to a subview

I am building a utility-based application, the data is stored in the MainViewController, and now I know how to pass data to the FlipsideViewController (many regards to this thread BTW, Sending data from Mainview to Flipside?). But I am getting the data onto an subview (subclass of UIView) that I have added to the flipside view. How can I pass data to this subview? I saw there is already a delegate and protocol set up in the FlipsideViewController.h, I am really new to the delegate sort of things. Any help would be greatly appreciated!
Updates:
On the main view, I have a couple of text fields for users to input to create an object. All the objects are stored in an array. Namely, my data is created and stored in the MainViewController. Now on the flip side, I have a custom UIView subclass which allows me to do my own drawing based on the data in that array. What I need to do here is pass the data that stored in MainViewController to this subview. Here is my relevant code:
In the MainViewController.m
- (IBAction)showInfo:(id)sender {
FlipsideViewController *controller = [[FlipsideViewController alloc] initWithNibName:#"FlipsideView" bundle:nil];
controller.delegate = self;
controller.receiver = data;//this is what I've done.
controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:controller animated:YES];
[controller release];
}
In the FlipsideViewController.h
#protocol FlipsideViewControllerDelegate;
#interface FlipsideViewController : UIViewController {
id <FlipsideViewControllerDelegate> delegate;
DataModel *receiver; //create a property to receive the data transferred from main view
}
#property (nonatomic, assign) id <FlipsideViewControllerDelegate> delegate;
#property (nonatomic, retain) DataModel *receiver;
- (IBAction)done:(id)sender;
#end
#protocol FlipsideViewControllerDelegate
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller;
#end
In the above code, "data" is an DataModel object declared in the MainViewController.h file.
And I want to do my custom drawing in drawing class (subclass of UIView), how can I pass data from the FlipsideViewControllerto this subview? Do I need to make use of delegate declared in the FlipsideViewController.h file? Thanks in advance!
I have had a quick look at the template and think you are getting confused with what the delegate is being used for.
The delegate in this template is not transferring data. When you have clicked the done button it calls back to MainViewController and asks it to call the dismissModalViewControllerAnimated method so that it can remove the view controller. This seems a bit superflous as the documentation states
If you call this method on the modal view controller itself, however, the modal view controller automatically forwards the message to its parent view controller.
Therefore you don't really need to call the parent to do this.
In Interface builder you can see that the FlipsideView.xib has it's File's Owner set to FlipsideViewController.xib.
Now if you right click the File's Owner you will see that view is connected to View this basically means that view is the name of the property in FlipsideViewController and View is the element in Interface Builder.
Therefore we can access elements in the xib file from FlipsideViewController using outlets.
To say draw a label you will need to do a couple of things
First add a property in the .h and synthesize it in the .m like
// FlipsideViewController.h
#interface FlipsideViewController : UIViewController
#property (nonatomic, retain) IBOutlet UILabel *testLabel; // <----- Added this
#property (nonatomic, assign) id <FlipsideViewControllerDelegate> delegate;
- (IBAction)done:(id)sender;
#end
// FlipsideViewController.m
#implementation FlipsideViewController
#synthesize delegate = _delegate;
#synthesize testLabel = _testLabel; // <----- Added this
// More methods
- (void)dealloc
{
[_testLabel release]; // Always do you memory management
[super dealloc];
}
Then back in Interface Builder
Add a UILabel element to your view
ctrl + drag from File's Owner to the UILabel you added
Select the label in my example it is testLabel
Now these are hooked up correctly. The place where you want to be setting the value of the label is in viewDidLoad: which you can now do like this
- (void)viewDidLoad
{
[super viewDidLoad];
self.testLabel.text = #"It Works"; // You would use the data passed in from `MainViewController`
}
I find the easiest way to pass data from one view to another is by directly setting the data in the next view from the original view.
For example;
In your FlipsideViewController.h, declare a 'container' for the data you want to pass. It must be the same class on both sides to work properly, ie. NSArray to NSArray, NSMutableDictionary to NSMutableDictionary.
NSMutableArray *newData;
...
#property (nonatomic, retain) NSMutableArray *newData; // This allows you to access this object from outside this class.
and in FlipsideViewController.m
#synthesize newData;
...
[newData release];
Now we need to pass the data, so to speak. Let's say the data we want to 'send' is stored in a NSMutableArray called 'results'.
In our MainViewController.m, when we are instantiating our next view controller (in this case FlipsideViewController) we can directly reference the newData mutable array after we initalize the nib.
FlipsideViewController *controller = [[FlipsideViewController alloc] initWithNibName:#"FlipsideView" bundle:nil];
controller.newData = results;
controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:controller animated:YES];
[controller release];
Make sure you are importing your FlipsideViewController in your MainViewController.h file.
If the property is declared in your .h file, you can pretty much reference the contents of the object from anywhere within the view stack!
Hope that helps :D

How to pass variables between 2 view conrollers

I have 2 view controllers now, And it both got tableviews.
When I choose a row in the second tableview (Using didSelectRowAtIndexPath),
and I want to pass the Information I got in the second View to the first View,
I tried to use delegate&protocol, but don't know why, It didn't work.
And I tried to use class method inside the first class, when I got variable in sencond View,
Call the class method inside the first class. The variable successfully pass to first View,
but when I want to set the Lable's text, it still failed..
Can somebody teach me how to do? thanks!
My protocol&delegate.
This is the second view.
#protocol CategoriesViewControllerDelegate;
#interface CategoriesViewController : UIViewController {
TableViewNewAppDelegate *appDelegate;
id <CategoriesViewControllerDelegate> delegate;
}
#property (nonatomic, assign) id <CategoriesViewControllerDelegate> delegate;
#end
#protocol CategoriesViewControllerDelegate <NSObject>
-(void)backstring:(NSString *)String;
#end
In the .m file , synthesize it
#implementation CategoriesViewController
#synthesize delegate;
didSelectRowAtindexPath
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
CategoryData *CateObj = [appDelegate.CateArray objectAtIndex:indexPath.row];
NSString *Strings = [NSString stringWithString:CateObj.CateTitle];
[delegate backstring:Strings];
[self.parentViewController dismissModalViewControllerAnimated:YES];
}
In the first view controller .h file.
#import "CategoriesViewController.h"
#interface DataController : UIViewController <CategoriesViewControllerDelegate>{
.m file
-(void)backstring:(NSString *)String {
NSLog(#"%#",String);
jCateField.text = String;
}
This is how I do my protocol+delegate. Are there something wrong?
btw, I created a Class method in the first view controller, and use the Class method in the sencond view controller, it succesfully pass variable to first view controller.
But the problem is, I can't set my Label's text inside my Class method, even calling Instance method to set text. Is there any way to solve this problem?
The code you provided seems to be correct. In your case you must set :
#property (nonatomic, assign) id <CategoriesViewControllerDelegate> delegate;
correctly to point to the first view controller which conforms to the protocol you defined :
#import "CategoriesViewController.h"
#interface DataController : UIViewController <CategoriesViewControllerDelegate>{
So it seems that you pushed a CategoriesViewController onto a first DataController, you probably missed to do so just before.
// self is the first view controller
// [myCategoriesViewController setDelegate:self]; old fashion
myCategoriesViewController.delegate = self;
[self presentModalViewController:myCategoriesViewController animated:YES];
This can probably solve your issue. Hope this helps.
Also consider let the first controller dismiss the second.
Here is a link to Apple's documentation.
You could just pass the information straight on to your second view controller;
SecondViewController.h
#interface SecondViewController
{
Information *info;
}
#property (nonatomic, retain) Information *info;
#end
SecondViewController.m
#implementation SecondViewController
#synthesize info;
...
#end
And in your didSelectRowAtIndexPath method;
SecondViewController *controller = [[SecondViewController alloc] initWithNibNamed:#"SecondViewController" bundle:nil];
[controller setInfo:YOUR_INFO_OBJECT];
[self.navigationController pushViewController:controller animated:YES];
[controller release];
Import second view controller header file in the first view controller implementation file. Import first view controller header file in second view controller header file.
Create the property (text/label/whatever) in the first view controller.
Create the property of first view controller in the second view controller.
Create the second view controller instance, set the first view controller property to what you need, push controller to the navigation controller. In the second view controller change whatever you want in the first view controller. Instance methods allowed. Do not forget to release first view controller.
Delegate pattern works in that way too.
View controllers are objects. Objects can have methods that can be called from other objects, and they can have instance variables. ("Delegate" is just a fancy term for this.)
There's no inherent reason why passing data between your view controllers should be hard or complicated, so long as the caller has the address of the callee. (And whether or not a given VC has an XIB is irrelevant.)
It sounds like your real problem is not knowing what to do with the data once it's been passed to the callee.
Stupid question: Is "jCateField" actually connected to the label you want to change, or is it nil? If you created the label from code (since you don't have an XIB), you will need to have stored the created label's address into "jCateField" during the view creation.
Can you post the code for as to ho you are displaying the contents when you come back to 1 st view controller.As here if the log gives you proper value then the issue is with the connection (if taken through iboutlet) or with addsubview .
Do you get nil in label or no value (label is hidden).

Call a method from a ModalView that is on the opener view

Basically I have a viewController that loads at app startup. In that VC, depending on whether there is userdata, I serve up a ModalView with either a login. After the user logs in, I can then dismiss the modalView, but I would like to call a method on the opener that will then populate a table with data.
I thought from the modalView I could do something like
[self.parentViewController loadInitialData];
[self.dismissModalViewControllerAnimated:YES];
but that does not work..
any suggestions?
The problem is because self.parentViewController is of type "UIViewController" and your -loadInitialData method doesn't exist in UIViewController. There are a couple of common ways to solve this problem... from easiest and least "correct" to most complicated and most "correct":
1) First you need to cast your self.parentViewController to the type of your parent view controller. Something like:
MyParentViewController *parentVC = (MyParentViewController*)self.parentViewController;
[parentVC loadInitialData];
2) You can add a property to your modal view controller that explicitly keeps a reference to your parent view controller and then call loadInitialData doing that.
#interface MyModalViewController : UIViewController {
MyParentViewController *myParentViewController;
}
#property (nonatomic, retain) MyParentViewController *myParentViewController;
Then you can call:
[self.myParentViewController loadInitialData];
3) The most complicated, but most correct way to do it is to create a Delegate protocol, have your ParentViewController implement the protocol and have your modal view controller keep a reference to the delegate and call that way. Something like:
#protocol ManageDataDelegate
- (void) loadInitialData;
#end
#interface MyParentViewController : UIViewController <ManageDataDelegate> { ...
#interface MyModalViewController : UIViewController {
id<ManageDataDelegate> delegate;
}
#property (nonatomic, assign) id<ManageDataDelegate> delegate;
When you present your modal view controller, just set the delegate. In your MyParentViewController:
MyModalViewController *vc = [[MyModalViewController alloc] init];
vc.delegate = self;
[self presentModalViewController:vc];
Then in your modal view controller you can call back like so:
[self.delegate loadInitialData];

How return a value from view2 to view1 when using navigation bar

I am New to the iPhone Development. How can i carry a string value from view2 to view1 when using navigation bar. I have no problem in carrying string values from view1 to view2 to view3 to....by using pushviewcontroller But when i come back to previous views using Back button of navigation bar, I cannot able to hold string values.
I need your help in solving this issue.
Thanks In Advance
This thing can be done easily if the pass the reference of the current class to the next class and change the values by using this reference.
Like:
The class that is to be pushed.
#interface B:UIViewController{
id delegate;
}
#property (nonatomic,retain) id delegate;
#end
#implementation B
#synthesize delegate;
-(void)methodA{
[delegate setNewString2:#"Madhup"];
}
#end
The class from which you are pushing B:
#interface A:UIViewController{
NSString *newString;
}
#property (nonatomic,retain) NSString *newString;
#end
#implementation A
#synthesize newString
- (void)method2{
B *newBObject = .......;
[newBObject setDelegate:self];
[self.navigationController pushViewCo.......];
}
#end
Hope this helps.
There's more than one way to do this. Here are a few:
You can access all the view controllers in the navigation stack via the navigation controller's viewControllers property: self.navigationController.viewControllers
You can reach the previous view controller (i.e. the one that pushed the current controller onto the navigation stack) via the parentViewController property: self.parentViewController
You can use the delegate pattern, where the previous (parent) view controller would be the current (child) controller's delegate.
You can keep a reference (retain) to the child controller in the parent controller.
In the first 3, the child controller would be responsible for handing the data to the parent controller. In the 4th, the parent would get the data from the child before releasing it.

NSMutableArray Vs UIView iphone

I have an NSMutableArray in my MainViewController classes.. I added 10 Objects to NSMutableArray.When i Click subview button in my MainViewController classes, I push SubviewController (UIViewController)classes.... In subviewcontroller classes i want to use MainViewController Values of NSMutableArray. And also i Want to add or remove objects from that NSMutableArray...
My aim is I want to use the NSMutableArray Values to all UIViewController classes to my project with an same name of the variable.
If I understand you right - you want to access the NSMutableArray in MainViewController from all your (subview) controllers?
You can store the handle to your MainViewController in all you subviews and access the array using that handle.
I think better idea would be move the data to some globally accessible instance (e.g. to ApplicationDelegate class or wrap it into singleton class)
What you want to do is add a Cocoa property in your main view controller that references the NSMutableArray instance which you instantiate and populate with elements.
In the header:
#interface MainViewController : UIViewController {
NSMutableArray *myMutableArray;
}
#property (nonatomic, retain) NSMutableArray *myMutableArray;
#end
In the implementation, add the #synthesize directive and remember to release the array in -dealloc:
#implementation MainViewController
#synthesize myMutableArray;
...
- (void) dealloc {
[myMutableArray release];
[super dealloc];
}
#end
You also want to add this property to view controllers that are subordinate to the main view controller, in the exact same way.
In your main view controller, when you are ready to push the subordinate view controller, you set the subordinate view controller's property accordingly:
- (void) pushSubordinateViewController {
SubordinateViewController *subVC = [[SubordinateViewController alloc] initWithNibName:#"SubordinateViewController" bundle:nil];
subVC.myMutableArray = self.myMutableArray; // this sets the sub view controller's mutable array property
[self.navigationController pushViewController:subVC animated:YES];
[subVC release];
}
Likewise in your subordinate view controller, it will need to set its subordinate's mutable array property accordingly, when it pushes its own view controller.
By setting references in this way, each view controller is pointed at the same mutable array, containing the desired elements.
To use the array, just call self.myMutableArray, e.g. [self.myMutableArray addObject:object].