I would like to get at
- (void)applicationWillTerminate:(UIApplication *)application
a variable from a view controller class.
I have build an tabbar application and only added the tabbar controller to the appdelegate.
[window addSubview:tabBarController.view];
How can i get an variable from the TestViewController:
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#interface TestViewController : UIViewController {
IBOutlet UILabel *testLabel;
NSString *currentString; //Value that i want to save at applicationWillTerminate
}
#property (nonatomic, retain) UILabel* testLabel;
#property (nonatomic, retain) NSString* currentString;
#end
It's somewhat incidental that TestViewController hasn't been dealloc'd by the time you reach applicationWillTerminate - it might make sense to store that value a level higher in your application. That approach would be to always store currentString in the UIApplicationDelegate so that you don't have to fetch it later:
#implementation TestViewController
- (void)setCurrentString:(NSString *)currentString {
((MyAppDelegate *)[[UIApplication sharedApplication] delegate]).currentString = currentString;
}
#end
Expanding on dbarker's answer, it sounds like what you really need is to save the currentString value in your data model. The proper place to do that is in the viewController itself.
If your data model is just that one string, you can create a property in the app delegate to hold it. Then the viewController writes to the app delegate property whenever the value of currentString changes in a view and/or its value when the view closes.
This way, the data (which is the entire point of the app anyway) is always in place when the app closes regardless of how many views you open.
It is the proper role of controllers to move information from the interface to the data model. Strictly speaking, the viewController shouldn't store any data at all beyond that needed by the interface itself. That should be a property of the data model which the viewControllers set by sending a message to the data model object with the values taken from the interface.
In this case, you would not have a currentString property in your view controllers. Instead they would have a property that is just a reference to the data model's currentString property. The view controllers would continuously update that property but would store nothing themselves.
The advantage of this design is obvious. If you need the value anywhere in your app, you have one location and one call to get it. No single part of the app needs to even know of the existence of any other part of the app save for the data model.
Not 100% sure what you are asking for but here is a guess:
UITabBarController's have a property called viewControllers which return all view controllers associated with the tabbar.
Assuming that the TestViewController was the first tab you could get to it by:
- (void)applicationWillTerminate:(UIApplication *)application {
TestViewController* test_view_controller = [tabBarController.viewControllers objectAtIndex:0] ;
NSString* value = test_view_controller.currentString ;
}
Note this would break if you decided to later move the TestViewController to a different position in the tabbar.
-- Edit --
Check all controllers and get the string from the controller that is of type TestViewController.
NSString* value = nil ;
for ( id unknownController in tabBarController.viewControllers ) {
if ( [unknownController isKindOfClass:[TestViewController class]] ) {
value = ((TestViewController*)unknownController).currentString ;
}
}
// value should be the value of the string.
Related
i've been doing a thing here using objective-c, but i'm a beginner in this language, and that's what i did:
there is a class, named "firstviewclass", that is in the control of my first view, in this view there is a textfield that the user puts a number. the textfield is in the firstviewclass.h named "setNumber". There is another class, named "secondviewclass" that is in control of the second view, in this view there is a label that is in the secondviewclass.h, and i want that this label recive the value that the user put in the textfield from the first view, but when i test it on the iOS simulator any number that i put in the textfield it appears in the label as 0... I really don't know what to do!
My codes:
firstviewclass.h:
#import <UIKit/UIKit.h>
#interface firstviewclass : UIViewController
#property (strong, nonatomic) IBOutlet UITextField *setNumber;
- (IBAction)gotonextview:(id)sender;
#end
secondviewclass.h:
#import <UIKit/UIKit.h>
#import "firstviewclass.h"
#interface secondviewclass : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *labelthatrecivesthetextfieldvalue;
#end
secondviewclass.m:
#import "secondviewclass.h"
#import "firstviewclass.h"
#implementation secondviewclass
#synthesize labelthatrecivesthetextfieldvalue;
-(void)viewDidLoad{
[super viewDidLoad];
firstviewclass *object = [[firstviewclass alloc] init];
NSString *string = [[NSString alloc] initWithFormat:#"%i", [object.setNumber.text intValue]];
labelthatrecivesthetextfieldvalue.text = string;
}
#end
First of all, I would strongly recommend not naming a property or instance variable setSomething. It will cause headaches for anyone reading your code because it will always look like you're trying to call a setter. Also, please do capitalize your class names.
Your actual problem is that in viewDidLoad you're creating an instance of firstviewclass, and then trying to get the value from setNumber. That is before the user had any chance to enter anything.
Also, the setNumber outlet in firstviewclass will probably be going nowhere anyway, since you're instantiating that class yourself, instead of loading the NIB.
Edit (ah, Storyboard, d'oh):
For Storyboard, you need to pass the setNumber text field's value to the second view.
First of all, remove the firstviewclass *object = [[firstviewclass alloc] init]; line.
Then, in your first view controller's prepareForSegue method, you can pass the value of the setNumber text field to a property in your your second view controller, and use it from there (e.g. in configureView).
I recommend working through Apple's Storyboard tutorial, it shows exactly what you need to do, step by step. The step you're having issues with right now, passing data to your next view controller, is here.
How are you initiating the setting of the label? From viewDidLoad? If so, then 0 would be correct because you have not set object.setNumber.text to anything. Also, you aren't carrying over the data from your first viewController. You need to let the second viewController know what the number is. Try setting this value in your NSUserDefaults, then in your second viewController, load that value from your defaults.
in the viewDidLoad of secondviewclass, you are creating a new instance of firstviewclass, and then accessing the property of the label. Since you haven't set that to be anything, it will always return zero.
Can you post the implementation of the gotonextview method?
EDIT:
I think you need to be setting the label property from the first view controller. so in your gotonextview method, add this line:
secondviewclass.labelthatrecivesthetextfieldvalue.text = self.setNumber.text;
I admit I'm a little lost in your naming scheme but that might work.
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).
I've got an array populating a small tableView in a DetailView class, and when the user presses a button I need the array to be sent to another View Controller, to populate a tableView there, but I'm having some difficulty getting it working. This is what I've been trying to do so far:
*DetailViewController.m*
#import "DetailViewController.h"
#import "OtherViewController.h"
-(IBAction) toCart:(id)sender {
OtherViewController *oVC = [[OtherViewController alloc] init];
oVC.shoppingList = sList;
NSLog(#"Ingredients count %d", [sList count]); //This returns a number, so the sList definitely contains values, and the method is definitely being called.
[oVC release];
}
*OtherViewController.m*
#import "OtherViewController.h"
#import "DetailViewController.h"
#synthesize shoppingList;
-(void) viewWillAppear: (BOOL)animated {
NSLog(#"list count: %d", [shoppingList count]); // This returns 0
}
sList is populated elsewhere in the class, and sList and shoppingList are both declared in their respective .h files, with #property (nonatomic, retain)...
Any help much appreciated!
As you are having taBbarcontroller, so you can proceed as follows :
Create references of your you viewControllers(which are associated with tabbar as topViewController) in your appDelegate.
otherViewController = [[tabBarController.viewControllers objectAtIndex:<tabIndex>] topViewController];
make it as #property in appDelegate so that you can access it anywhere in your app.
now,
-(IBAction) toCart:(id)sender {
//appDelegate <--- get reference to your application delegate using [[UIApplication sharedApplicaiton]delegate] do not forget to properly type cast it.
OtherViewController *oVC = [appDelegate otherViewController];
oVC.shoppingList = sList;
NSLog(#"Ingredients count %d", [sList count]);
//This returns a number, so the sList definitely contains values, and the method is definitely being called.
// [oVC release]; no need to release it...
}
//also make sure you do not initialize shoppingList of otherViewController in viewDidLoad(or any other method) of otherViewController, else it will be overwritten(lost its previous reference).
in your appDelegate's .h write
#property OtherViewController *otherViewController;
in appDelegate's.m
#synthesize otherViewController;
in appDelegates's .m (method didFinishLaunchingWithOptions: ) write
otherViewController = [[tabBarController.viewControllers objectAtIndex:<tabIndex>] topViewController];
Thanks
In toCart:, you are creating an OtherViewController and then immediately throwing it away. Whatever OtherViewController is calling -viewWillAppear, it isn't the one you're creating in toCart:. How is that object created and put on the screen? You need a pointer to it to modify it.
Better, though, would be to move your model data out of the view controllers and put it in a single ShoppingCart object. Then all your view controllers would have a reference to it (or you can make ShoppingCart a singleton if that makes sense in your program). This way, any time you change the shopping cart from anywhere, all views will correctly update without having to tell every view controller about every other view controller.
Okay, I've tried to figure this out over and over again.
I know the best practice is to have the App Delegate pass the managed object context to the first view controller in an application, and then have each subsequent view controller pass the managed object context down. However, when I'm using a Tab Bar Controller in my application, I can seem to wrap my head around that extra layer.
The only way I've been able to figure out how to do it is have the root view controller of each tab "Reach Back" into the app delegate to grab the context, but as I understand it this is poor form.
You can use interface builder to achieve the same thing.
Here is a slightly modified (for some additional clarity) version of Rog's original suggestion - notice the IBOutlet's
#interface AppDelegate : NSObject <UIApplicationDelegate> {
ViewController1 *vc1;
ViewController2 *vc2;
ViewController3 *vc3;
}
#property (nonatomic, retain) IBOutlet ViewController1 *vc1;
#property (nonatomic, retain) IBOutlet ViewController2 *vc2;
#property (nonatomic, retain) IBOutlet ViewController3 *vc2;
Then on the implementation file:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
vc1.managedObjectContext = self.managedObjectContext;
vc2.managedObjectContext = self.managedObjectContext;
vc3.managedObjectContext = self.managedObjectContext;
// Continue with your implementation logic
}
Then from within Interface Builder ctrl drag from your App Delegate to the View Controller nested within the Tab Bar Controller and hook up the relevant View controller from the contextual menu that appears.
The key was, in the end, not to rely on interface builder to build the tab bar controller. By doing it manually in code I'm able to easily pass the managed object context to the view controller as I create them in applicatoinDidFinishLaunchingWithOptions:
I used this article as my basis: http://www.iphonelife.co.uk/creating-a-uitabbarcontroller-programmatically/
You can also just do something like this in your AppDelegate:
CoreDataUsingViewController *vc = (CoreDataUsingViewController *)[[tabBarController viewControllers] objectAtIndex:1];
vc.managedObjectContext = self.managedObjectContext;
I was adding coreData to an existing project with a few different build targets and didn't want to recreate all the different UITabBarControllers from scratch. It was pretty easy to do this way, though I'm not sure if it's the most artful way to do it or not.
See also
How to share a ManagedObjectContext when using UITabBarController
Not sure if I understand your issue correctly but why not simply pass the MOC to the other view controllers in the same manner? Here's an example:
#interface AppDelegate : NSObject <UIApplicationDelegate> {
ViewController1 *vc1;
ViewController2 *vc2;
ViewController3 *vc3;
}
// Declare properties as per normal
Then on the implementation file:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
vc1.managedObjectContext = self.managedObjectContext;
vc2.managedObjectContext = self.managedObjectContext;
vc3.managedObjectContext = self.managedObjectContext;
// Continue with your implementation logic
}
I hope it helps!
Rog
I'm trying to share an NSArray object between several different view controllers and my first thought was to add a property on the app delegate as they all have access to this object.
But after some debugging it appears I can't actually share this array for some reason. When I set the object from the first view controller and NSLog the results all is well. But when I attempt to get that array value using another view controller object it always returns UITouchData (not the value previously shown in the logs after my first view controller set the value)
Here is the code that I'm using to set the value
NSArray* cookies = [NSHTTPCookie
cookiesWithResponseHeaderFields:[response allHeaderFields]
forURL:[NSURL URLWithString:#""]];
[appDelegate setAuthCookie:cookies];
Here is part of the .h for my app delegate
#interface SomeAppDelegate : NSObject <UIApplicationDelegate> {
NSArray* authCookie;
}
#property (retain) NSArray* authCookie;
- (void)setAuthCookie:(NSArray *)cookie;
- (NSArray *)getAuthCookie;
#end
Here is the .m for the methods in question
#synthesize authCookie;
- (void)setAuthCookie:(NSArray *)cookie
{
authCookie = cookie;
}
- (NSArray *)getAuthCookie
{
return authCookie;
}
Here is the attempt to grab this array in the second view controller that fails (technically it doesn't fail on this line but I don't get an NSArray back as expected so when I try to use this it fails)
NSArray* cookies = [appDelegate getAuthCookie];
Any way I can share state using the app delegate like this?
Your memory management is wrong, and you are getting a completely different object which has inherited the old array's address when you later use the getter.
Your #property is correct, but you've written your own setter and getter that do not retain the object. You don't need to use both #property/#synthesize and supply your own getter/setter. The former is a newer means of automating the latter.
If you remove your implementations of setAuthCookier: and getAuthCookie then your code should work.