Accessing a view controller's function/variable from other view controllers - iphone

I'm currently making the initial menu view controller, which sets up the setting for main game view controller..
so in my menu game controller, I have
#import "MainGameViewController.h"
#implementation menuViewcontroller
......
-(void)setting{
NSMutableDictionary *regions =
[(MainGaimViewController *)self.delegate regions];
NSNumber *yesBool = [NSNumber numberWithBool:YES];
NSNumber *noBool = [NSNumber numberWithBool:NO];
[regions setValue:yesBool forKey:#"KM"];
[regions setValue:noBool forKey:#"KF"];
}
but this gives me the "Request for member 'delegate' in something not a structure or union
regions is a NSMutableDictionary in main game view controller.
so I think menuViewController is not being able to access the function/variable in main game view controller despite the import.
currently, I haven't declared "class MainGameViewController" in my menu implementation file. Could that be why? should I make an object of maingameviewcontroller and use it?
What could be wrong?
Please help me out..

I find it is much cleaner and clearer to put application-global data in separate singleton classes rather than wedge them in to the application delegate object and to try passing around pointers to root-level view controllers. View Controllers should focus just on the job of managing its view and responding to actions, and interacting with data models through the view. The data models themselves and app global data generally should sit outside of ViewControllers. The singleton pattern works nicely for data management, as the data is easily available to any piece of code in the app that needs it without having to worry about setting up delegate protocols, or whether a view controller owning data is valid any more.
You can see my answer to this question for how to set up a singleton data manager class:
Objective C: store variables accessible in all views

Related

Showing the same UIView instance in different UIViewControllers

Is it possible to somehow store a UIView instance in such a way that it can be accessed from other view controllers? I know this is probably bordering on "globals" which they say are bad, but I digress. All I know is I have a couple UITabBar tabs that need to reference the same instance of a view that was instantiated in one tab and needs to be displayed again in another tab. What's the best approach for doing something like that?
Sure. You just need to store a retained reference to the UIView object in a persistent object. For example, you can add a retained property to your UIApplicationDelegate subclass. You can have that delegate instantiate the view, and all the controllers would just ask the app delegate for the view. If you have a root view controller that is always available, you could retain it there.
Maybe thinking through the overall structure of your app can help find the "right" place to store the UIView. Below I present an app structure I frequently use, not necessarily as advice on how you should structure your app, but as an example to expand the options you can consider to help you with thinking about the best structure for you app.
I write a lot of brochure like apps for our clients. In these apps I need to present a number of views, each somewhat analogous to pages in a brochure. Some of these views will have user interaction, and need to retain their state when off screen, or share the state data with other views.
To manage these apps I create a presentation manager object. This object will retain the overall state of the app, including views that must persist when not displayed. I use one master UIViewController that owns the presentation manager and is responsible for all common view control operations. The specific logic for individual views will go in UIView subclasses for each view. These individual views are created by the presentation manager, and can ask that manager for what it knows, including any persistent views.
You can just use dependency injection, to inject the same view instance to the view controllers like this:
UIView *myView = [UIView new];
UIViewController *controller1 = [UIViewController new];
UIViewController *controller2 = [UIViewController new];
controller1.view = myView;
controller2.view = myView;
[myView release];
B/c you use UITabBar I would suggest to add your custom view to the window in the app delegate. Then you don't have to store it, just hide it. You can use either NSNotificationCenter to send notifications to show the view or you can call your appDelegate and show the view manually.

Where should I put my NSFetchedResultsControllerDelegate?

I am teaching myself to program by making a simple core-data drill-down app with a UINavigationController where you select a grandparent entity to see a UITableView of parents, and then select a parent to see children. Whenever the user selects an item, I use properties to hand over the NSManagedObjectContext and NSFetchedResultsController to the next view. Each view controller is a UITableViewController, and they all conform to the NSFetchedResultsControllerDelegate Protocol.
This works fine, but means every view controller is implementing the delegate methods etc., which seems inefficient.
To make the app simpler, would it be better to have a single NSFetchedResultsControllerDelegate that is referenced by all my view controllers? And where would the best place for this be - the app delegate?
Thanks!
---------------------------EDIT----------------------------
I'm trying to get GorillaPatch's answer to work below. In my child view, I have this method which is a delegate method for the modal "Adding View Controller":
- (void)addingViewController:(AddingViewController *)addingViewController didAdd:(NSString *)itemAdded
{
NSManagedObjectContext *context = [parent managedObjectContext];
Child *newChild = [NSEntityDescription insertNewObjectForEntityForName:#"Child" inManagedObjectContext:context];
[self.children insertObject:newChild atIndex:0];
newChild.name = itemAdded;
newChild.dateStamp = [NSDate date];
// Save the context.
NSError *error = nil;
if (![context save:&error])
{
// Handle The Error.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
[self dismissModalViewControllerAnimated:YES];
}
And there is the following in the header file:
#property (nonatomic, retain) Trip *trip;
#property (nonatomic, retain) Checklist *checklist;
#property (nonatomic, retain) NSMutableArray *checklists;
In my humble opinion, definitely no. In general, you need a different NSFetchedResultsControllerDelegate implementation for each view, because what you actually do in each delegate method may and will usually differ (unless your application is really simple).
I would recommend using a FetchedResultsController instance for each UITableView. The reason is because of the way that the FRC responds to changes of data.You can read about the FRC here: http://developer.apple.com/library/ios/#documentation/CoreData/Reference/NSFetchedResultsController_Class/Reference/Reference.html
If you set a delegate for a fetched
results controller, the controller
registers to receive change
notifications from its managed object
context. Any change in the context
that affects the result set or section
information is processed and the
results are updated accordingly. The
controller notifies the delegate when
result objects change location or when
sections are modified (see
NSFetchedResultsControllerDelegate).
You typically use these methods to
update the display of the table view.
Besides, if you're drilling down as you said, each new child view should be a unique set of data that would not allow you to share the FRC.
No, you'll probably be better off by writing a base class that implements NSFetchedResultsControllerDelegate (which would probably be a UITableViewController subclass) and contains a NSFetchedResultsController instance, and then extending your base class wherever you need it.
If you have multiple levels to drill down to, the most likely scenario is that the only thing that will change among your implementations is the predicate used to obtain your NSFetchedResultsController instance.
Why do you hand over the NSFetchedResultsController and the NSManagedObjectContext (MOC) to the child or detail view controller? I would strongly suggest defining a property on the detail view controller, which is the object you want to show.
For example if you have a list of recipes fetched from CoreData and you tab on a recipe, you would have a detail view controller sliding in which would show the recipe's details. I would suggest implementing it by having a UIViewController subclass which has a currentRecipe instance variable. You would then set this instance variable to the recipe which you tabbed in your list and then push the view controller on the stack.
By doing this you would decouple your user interface really nicely. This makes this view controller reusable in the whole program.
Update
Due to our lengthy discussion I would like to provide more material which could be helpful if you want to know more about MVC design patterns and how to implement a drill down navigation on the iPhone.
Sample code: Have a look at the iPhoneCoreDataRecipes app which was demoed on WWDC09 and WWDC10 to illustrate how to implement a stack of detail view controllers and how they interact with each other.
WWDC session videos: there are some WWDC session videos which could be helpful:
WWDC10: Session 116 - Model-View-Controller for iPhone OS
WWDC09: Session 125 - Effective iPhone App Architecture
To expand on the previous answers:
The NSFetchedResultsController is part of the controller layer of a Model-View-Controller app design. The name of the design should be Model-Controller-View because the controller mediates between the data model (Core Data in this case) and the view. As such the FRC has to be customized for the needs of each particular tableview whose data it fetches, sorts and manages. It properly belongs in the tableview's datasource delegate which is usually just the tableview controller object.
The design you are contemplating would only work if every single table used the exact same entity with the exact same sort order. In that case, why bother with multiple tables?

How to properly pass NSArray / NSMutableArray from one UIViewController to another

An iphone app I'm working on is terminating periodically ... and I have a feeling it is because of how I'm accessing a shared NSArray amongst several view controllers. So ...
What is the best way to pass NSArray/NSMutableArray amongst several ViewControllers ... all of which can modify and/or set it to nil or whatever?
Thanks
There are two approaches, generally speaking.
In the first approach, when you instantiate your next view controller, you could set an array property to "inherit" from the current (soon-to-be previous or parent) view controller:
MyNextViewController *_myNextViewController = [[MyNextViewController alloc] initWithNibName:#"MyNextViewController" bundle:nil];
_myNextViewController.myViewControllerArray = self.myViewControllerArray;
...
In the second approach, you could create a myAppDelegateArray property in your application delegate, instantiating the array when the application initializes (or instantiated somewhere else, on demand).
You can then call the getter for this property from any class — including view controllers.
For example, within an instance of MyNextViewController, you might have a property called myViewControllerArray and you would set it as follows:
self.myViewControllerArray = [UIAppDelegate myAppDelegateArray];
You would add a #define statement somewhere in your application constants file, e.g.:
#define UIAppDelegate ((MyAppDelegate *)[UIApplication sharedApplication].delegate)
or use the full [UIApplication sharedApplication].delegate call, if you prefer.
As a general approach, people seem to prefer the app delegate approach, as it decentralizes access to the desired property.
With this approach, if you rearrange your view controllers or insert new view controllers into your VC hierarchy, you wouldn't need to debug child view controller properties inherited from parent view controllers, because a reference is always available from the app delegate.
you are probably not retaining it, it gets deallocated and you are trying to access it again. try to send a [myArray retain] where you are using it (in each of the controllers), and [myArray release] when you are done with it

iPhone: How to Pass Data Between Several Viewcontrollers in a Tabbar App

I have following problem:
I have built a tabbar application with 4 tabs. I want to pass a object/variable from the first tab controller to the third one and initialize this controller with the corresponding object.
I've already done some research. The best way, corresponding to a clean model approach, would be to call some initWithObject: method on the called viewcontroller.
How can I achieve this? How can I call the init method of the receivercontroller within the callercontroller? Can you give me some code example?
Edit:
To pass data between several views/classes etc simply create some Kind of data class which holds the data beeing shared between several classes. For more information follow the link:
Singleton
You need a data model object that stores the data for application.
A data model is a customized, standalone object accessible from anywhere in the application. The data model object knows nothing about any views or view controllers. It just stores data and the logical relationships between that data.
When different parts of the app need to write or read data, they write and read to the data model. In your case, view1 would save its data to the data model when it unloads and then view2 would read that data from the data model when it loads (or vice versa.)
In a properly designed app, no two view controllers should have access to the internal data of another controller. (The only reason a view controllers needs to know of the existence of another controller is if it has to trigger the loading of that other controller.)
The quick and dirty way to create a data model is to add attributes to the app delegate and then call the app delegate from the view controllers using:
YourAppDelegateClass *appDelegate = [[UIApplication sharedApplication] delegate];
myLocalProperty = appDelegate.someDataModelProperty;
This will work for small project but as your data grows complex, you should create a dedicated class for your data model.
Edit:
To clarify for your specific case, you would add the call to the data model when the receiver viewController becomes active.
Placing the data in an init method or a viewDidLoad won't work because in a UITabBar the users can switch back and forth without unloading the view or reinitializing the view controller.
The best place to retrieve changing data is in the viewWillAppear controller method. That way the data will be updated every time the user switches to that tab.
You might want to consider NSNotificationCenter (Reference); you register the one viewcontroller with the application notification center, and send a notification when a selection is made. When the notification is received, the other viewcontroller updates itself accordingly.
I don't think this is best practice (also check syntax) however I have got away with:
in the .h
otherclassref *otherclassname
#property (assign) otherclassname otherclassref;
and in the .m
#synthesize otherclassref;
then I just assign the reference from somewhere convenient e.g. the app delegate or wherever you are instantiating your viewcontrollers.
then the view controller can get a reference to the other view controller.
I add #class secondviewcontroller to the .h file for the firstviewcontroller and put put the #imports "secondviewcontroller.h" in the .m file of the first view controller. These are called forward references and prevent compiler errors resulting from having .h files referencing each other.

How do I access the managedObjectContext from a controller deep in the UI?

I'm still a little fuzzy on understanding iPhone/Cocoa in general so this is probably a simple question.
I have a CoreData Window-Based App for the iPhone. The rootController is a UITabBarController. The first tab view has a UINavigationController attached to it with a table in it's main view.
When the App starts the objectContext is set up, which makes sense to have the App do that once. But now I have the managedObjectContext in the main Controller but I want to get that passed down to the Controller of the View inside the navcontroller, inside the first item in the TabBarController's tab list. How do I do this?
Would naming the one of the fields in the UI Inspector Tool allow me to do something like:
tabcontroller.navcontroller.manageObjectContext = self.managedObjectContext;
Would this only work if the controller was instantiated and 'live'. (Do the controllers not get instantiated until they are needed?) What if this was in a view that was for the most part hidden?
Anyway this is probably a simple thing I'm just not understanding things properly yet.
What is the general right way to share the manageObjectContext that is created and setup in the rootController to the many sub-controllers in the app?
I'm guessing this is the preferred method assuming the core-data initialization is done in the AppDelegate:
[[[UIApplication sharedApplication] delegate] managedObjectContext]
I usually give controllers a - (id)initWithManagedObjectContext:(NSManagedObjectContext *)context init method and a corresponding variable.
If a controller creates another controller in turn, it will, if needed, pass the NSManagedObjectContext to that controller in the same manner.
If you don't want to create an extra init method, just give the controllers a property for the NSManagedObjectContext and set that property directly after creating them.
I usually try to limit the number of controllers that directly deal with and "know about" Core Data though.
The answers to this question provide several means of accessing the Core Data stack deep within your application. As I indicate in one of the comments, I prefer to use a singleton DatabaseController that can be accessed from wherever, similar to how the NSUserDefaults' standardUserDefaults works.