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

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];

Related

Accessing viewController properties from the app delegate

I am quite new to iOS programming so please be nice :) I am trying to google out this for hours now with no success. I have setup an iOS master detail project.
What i need to do. is to change a label in the detailViewController when the app calls applicationDidEnterBackground
This is my faulty code in the appdelegate applicationDidEnterBackground method
UIViewController *temp = [self.navigationController visibleViewController];
NSLog(#"%#",[temp nibName]);
if ([temp nibName] == #"DetailViewController") {
temp._lblBrewingTime = #"";
}
This doesnt work. semantic issue: lblbrewingtime not found on object of type UIViewController.
If I add a breakpoint and check the structure of the temp pointer. I can see the _lblBrewingTime type.
Can you please point me how to get the properties of whatever view is currently loaded in the app delegate?
thank you very much,
Greets,
Nick
You have to explicitly cast it to DetailViewController, once you are sure that the visibleViewController is DetailViewController actually.
So here's the fix:-
UIViewController *temp = [self.navigationController visibleViewController];
NSLog(#"%#",[temp nibName]);
if ([temp nibName] == #"DetailViewController") {
DetailViewController* tempDVCObj = (DetailViewController*)temp;
//temp._lblBrewingTime = #"";
tempDVCObj._lblBrewingTime = #"";
}
And it says absolutely correct that your property _lblBrewingTime is not the property of UIViewController, it's the property of DetailViewController i.e. a subclass of UIViewController.
Some things here:
You should keep a reference to your main controller in the AppDelegate and access the view through this reference - the visible view controller in the navigation controller may not be your view controller class, e.g. because you navigated to another view.
You access the view controller via the UIViewController interface. The UIViewController class does not know about your child view controller's properties, so it cannot access the _lblBrewingType. You have to use your view controller's class name to access its properties, e.g. MyViewController * myVc = (MyViewController*)viewController.
_lblBrewingType looks like an internal variable of your view controller. To access it from the outside, you must provide it as a property:
// MyViewController.h
#interface MyViewController : UIViewController
{
UILabel* _lblBrewingType;
}
#property (strong, nonatomic) IBOutlet UILabel *lblBrewingType;
And the implementation:
// MyViewController.m
#implementation MyViewController
#synthesize lblBrewingType;
#end

how to pass value when popToRoot

will like to know how do i pass value back to the root view controller when i popToRoot.
introVideoViewController *intro = [introVideoViewController alloc];
intro.fromReset =1;
[self.navigationController popToRootViewControllerAnimated:NO];
With the VC that you want to pop back from, you need to give it a delegate property -
#class MyViewController;
#protocol MyViewControllerDelegate <NSObject>
-(void)myViewControllerDidDismiss:(MyViewController *)controller withSomeObject:(id)someObject;
#end
#interface MyViewController : UIViewController
#property (nonatomic, assign) id<MyViewControllerDelegate> myViewControllerDelegate;
#end
...and in the root VC you make it conform to that protocol, and implement the dismiss method -
-(void)myViewControllerDidDismiss:(MyViewController *)controller withSomeObject:(id)someObject {
// now I've got the object from the VC I just popped
}
Forgot to mention that you need to call myViewControllerDidDismiss:withSomeObject: when you pop the VC.
Edit - Also forgot to mention that you need to set the VC's delegate as your root VC when you create it, or else it'll be trying to call nil when you pop back -
[myViewController setMyViewControllerDelegate:self];
Just use the below code
NSArray *arr = [self.navigationController viewControllers];
CLASS_OF_ROOT_VIEW_CONTROLLER *rvc = (CLASS_OF_ROOT_VIEW_CONTROLLER *)[arr objectAtIndex:0];
rvc.variable = value;

Call a parent view controller (through a navigationcontroller)

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.

Return NSString from UIViewController

I want to return a NSString * from a UIViewController, called InputUIViewController, to the previous UIViewController, called CallerUIViewController, which started InputUIViewController. I want to do it just before or when InputUIViewController calls:
[self dismissModelViewControllerAnimated:YES];
Is there a standard way to do this?
The standard way to do this would be to use a delegate.
In your InputViewController add a new delegate protocal, and a property for your delegate.
Then in your CallerUIViewController implement the delegate. Then just before your dismiss the modal view controller you can call back to your delegate.
So your InputViewController might look like this:
#protocol InputViewControllerDelegate;
#interface InputViewControllerDelegate : UIViewController {
}
#property (nonatomic, assign) id <InputViewControllerDelegate> delegate;
#end
#protocol InputViewControllerDelegate
- (void)didFinishWithInputView:(NSString *)stringValue;
#end
The method that dismisses the modal view would look something like this:
-(void)dismissSelf
{
[self.delegate didFinishWithInputView:#"MY STRING VALUE"];
[self dismissModalViewControllerAnimated:YES];
}
Then in your CallerUIViewController you would implement the InputViewControllerDelegate and the didFinishWithInputView method.
The CallerUIViewController header would look something like:
#interface CallerUIViewController : UIViewController <InputViewControllerDelegate> {
}
and your didFinishWithInputView method would be implemented something like:
- (void)didFinishWithInputView:(NSString *)stringValue
{
// This method will be called by the InputViewController just before it is dismissed
}
Just before your present the InputViewController you would set the delegate to self.
-(void)showInputViewController
{
InputViewController *inputVC = [[InputViewController alloc] init];
inputVC.delegate = self;
[self presentModalViewController:inputVC animated:YES];
[inputVC release];
}
You can do this by simply creating a NSString object as property in prvious view controller and when in second view you call dismissModelViewControllerAnimated then before it assign value to previous view controller property. This might help you -
Passing data between classes using Objective-C

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).