How to access one UIViewControllers properties from another UIViewController? - iphone

I have one single MainViewController which has of course it's one main UIView. I have this main view comprised of many different subviews.
Some of these subviews have their very own ViewController.
Lets say the MAIN view (whose delegate is primarily MainViewController) has a container which loads another UIView that uses a separate UIViewController- SecondaryViewController as the delegate for most it's actions.
This container view is of course loaded in MainViewController via
MyContainerViewController *myContainerController =
[[MyContainerViewController alloc] ...];
[self addSubView: myContainerController.view];
the controller for myContainerController.view though is MyContainerViewController. How inside this controller do I access MainViewController properties? Specifically I need to access MainViewController's - self.navigationController property to push a new ViewController? :)
Does this make any sense? I assume there's going to be casting of some sort involved since it seems I need to somehow retain a reference to MainViewController inside SecondaryViewController?

It doesn't make sense to push a new ViewController from the SecondaryViewController in the MainViewController.
This screws up the design of the code. A child object will access its parents method to call a method. By other words: bad code.
Make a delegate call from the SecondaryViewController to the MainViewController that it state has changed. Then the MainViewController can decide to do with the call and the SecondaryViewController will not know anything about the implementation of the MainViewController.
So:
Make a protocol in SecondaryViewController.
Let the MainViewController be SecondaryViewController's delegate.
Implement the delegate method in MainViewController which pushes the new ViewController.

Expose the desired sub-view controllers as properties of the view controller that contains them.
Expose your root view controller(s) as properties of your app delegate, and synthesize them also.
When you want to access a different UIViewController, first obtain a reference to your appDelegate:
MyAppDelegate* myAppDelegate = [[UIApplication sharedApplication] delegate];
Then just use your chain of properties to get access to your desired view controller.
SubSubViewController* ssvc = myAppDelegate.rootViewController.subViewController.subSubViewController;
Some folks frown upon the use of the sharedApplication class method to obtain the reference to a delegate, but I've used it in multiple apps and not suffered for it (yet). The alternative is to pipe your appDelegate through your hierarchy of viewControllers.

Related

passing data back to a previously allocated UIViewController

I have a view controller that then has a button that passes to an option menu.
When options are set they need to be past back to the previously allocated viewcontroller.
How is this possible without 'alloc and init another' instance of the object?
You can achieve this by using a delegate protocol. First view controller should become the delegate of the second view controller and then you can call this delegate method in your first view controller once the selection is done.
You can lookup google for implementation of delegates in objective-c. Its pretty simple. Add a
#protocol <delegatename>
<declare delegate method>
#end
Create a member variable in the second view controller for assigning the delegate. And define the method in the class implementing the delegate.
When you init your option viewController, pass it a reference to its parent.
I.E.
[[OptionViewController alloc] initWith...: parent:];
Use a #property or a method or somesuch to call on the parent to pass the data back.
You could use a shared singleton?
http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html
Or save the parameters to nsuserdefaults and read them back in in your first viewcontroller
or some other temporary store such as your appdelegate
I've used all three of the above approaches before.

How to pass NSManagedObjectContext via Interface Builder's XIBs

I have a simple iOS application with one UIViewController beneath a UINavigationController. The UIViewController has an IBOutlet for an NSManagedObjectContext.
The AppDelegate has an IBOutlet for the nav controller - but not the view controller. The view controller is automatically instantiated ala the XIB process (as a child of the nav controller).
With this setup, how does one cleanly assign or pass the app delegate's NSManagedObjectContext to the view controller's IBOutlet property. There is a nav controller in the way :) and the app delegate doesn't have a direct property for the UIViewController.
It is a weird problem in that, I want to link a property from one XIB component to another component's property. Most of the XIB work I've done takes a property and points it to an object in the XIB which in turn - gets instantiated ala the normal process but in this case, the context is being created correctly in the app delegate, I just want to pass it on to the view controller when it instantiates it.
You don't need to pass it, just grab it from the app delegate as required:
#import "MyAppDleegate.h"
NSManagedObjectContext* moc = [(MyAppDelegate*)[UIApplication sharedApplication].delegate managedObjectContext];
Apple's docs recommend that you pass references to your managed object context to the classes the require them instead of referencing it from your app delegate.
Here's what the application:didFinishLaunchingWithOptions: looks like in one of my Core Data projects.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
LocationsViewController *lvc = (LocationsViewController *)self.navigationController.topViewController;
lvc.managedObjectContext = self.managedObjectContext;
assert(lvc.managedObjectContext != nil);
[self.window addSubview:self.navigationController.view];
[self.window makeKeyAndVisible];
return YES;
}
You'll see that I also start with a UINavigationController with a single root view controller.
You've got the right idea, but the problem you're wrestling with seems to be entirely of your own creation. You say that your app delegate has an outlet for the navigation controller, but not for the nav controller's root view controller, because you've set up your nib such that the view controller is created when the nib is loaded. There's nothing wrong with that, but there's also no reason that the app delegate shouldn't have an outlet for that controller. Indeed, the entire reason for outlets is to get references to things that are loaded from a nib.
Add an outlet to your app delegate for your root view controller, and connect it. The app delegate can then give the controller a reference to the managed object context.
With respect to your question about multiple view controllers, I wonder what sort of real-world app might have view controller (A), which needs data, load another view controller (B) which doesn't need any data, followed by a third (C) which again needs data? A realistic example might help, if you have one.
Remember that you don't have to pass the entire managed object context to each successive view controller. You can instead pass just the part of the model that the controller will need to do its work by passing a managed object.

Call rootViewController to switch views within content view (iOS)

I'm working on a pretty simple multiview app for the iOS and I've been following a great tutorial in an Apress book. I've basically got my rootViewController instantiated and displayed with the app delegate, and I've got a number of content viewControllers (6) which I'd like to swap in and out based on user input. However, in the book they perform their switches with a button on a toolbar placed in the rootView using Interface Builder. It fires a method in rootView that loads up the new content ViewController and displays it.
My problem is that I'd like to perform the content view switch (that lies in my rootViewController instance), but I'd like to trigger the switch action with a button that's in my content view (and is therefore unavailable as my File Owner is my contentViewController, whose reference is held inside my rootViewController).
Hopefully I've explained it well enough, please let me know if I should elaborate more. I appreciate any help!
You need to pass down a reference to your root view controller (RootViewController *rootViewController) when you create your content view either in a custom init method or by just assigning it after you created it: self.contentView.rootViewController = self;.
Now inside your content view you can then call the appropriate method in the root view controller to do the switch: [self.rootViewController switchView]. This call then can be triggered inside the method that is called when you press the button (IBAction method).
So this is what you need to do:
1) Create a property inside the your content view controller of type RootViewController
#class RootViewController;
#interface MyContentViewController : NSObject {
#private
RootViewController *rootViewController;
}
#property (retain) RootViewController *rootViewController;
and make sure it retains the reference.
2) Synthesis the property and add the callback to the root view controller that switches the view:
#implementation MyContentViewController
#synthesize rootViewController;
- (IBAction) switchView:(id) sender {
[rootViewController switchToNextView];
}
-(void) dealloc {
[rootViewController release];
[super dealloc];
}
Also release your retain reference at the end.
3) Assign the root view controller to the content view inside your RootViewController:
self.contentViewController = [[[MyContentViewController alloc]
initWithNibName:#"ContentView"
bundle:nil] autorelease];
self.contentViewController.rootViewController = self;
That should be all. I hope that helps you.
Well, you could simply create an IBAction in each of your child controllers that calls:
[self.parentViewController switchToDifferentController:(int) viewNumber]
and then implement the switchToDifferentController method in your root. Other than ignore the compiler warning that parentView might not implement that method, it might work.
However, that is a bit brittle, as you'd have to assume that it was the parent calling you and that nobody will forget to implement that method.
In general, you use the "delegate" concept for a child controller to ask its parent to do something. The general idea is that you declare a group of methods as a "protocol". Think of it as a contract between objects. One object can say "I promise to implement these methods," and another can then choose to send those messages to it. The contract allows the compiler/system to check for conformance. You'll see this in UITableView, where the OS provides a standard table, but it calls back to your code to provide the individual cells as needed.
To implement a protocol, you mustdo the following: (See code segments below
Declares a protocol for the conversation
Specify that the parent will follows that protocol
Create a delegate property in your child
When the parent is about to launch the child, it assigns itself as the delegate for that child.
When the child wants to switch, it calls the parent using that protocol
#protocol myVCDelegate
- (void)switchToDifferentController:(int) viewNumber ;
#end
#interface ParentViewController : UIViewController <VCDelegate>
#property(nonatomic, assign) id <VCDelegate> delegate
childController.delegate = self;
[self.delegate switchToDifferentController:kController5];

accessing view controller's view

I am inside a class on a view-based app, one that was creating with one view controller.
WHen I am inside the view controller I can access its view using self.view, but how do I access the same view if I am inside a class?
[[UIApplication sharedApplication] delegate]... //??? what do I put here?
thanks
Assuming that the view controller is a member of your application delegate. you can access it like this: ("YourAppDelegate" should be replaced with the actual type name of your application delegate)
( ( YourAppDelegate *) [ [ UIApplication sharedApplication ] delegate ] ).viewController.view;
If you're trying to give a singleton a reference to your main viewController, a better solution might be to get your MyAppDelegate to set a viewController property on your singleton at launch:
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
[MySingletonClass sharedSingleton].viewController = self.viewController;
}
If you plan on re-using a Singleton like this in multiple projects, it often makes more sense to set these sort of properties from your AppDelegate.
A subview really shouldn't be manipulating a parent view, each class should manage it's own view.
Anyway, if your class is a subclass of UIView and is a subview of the viewController, you can get the viewController's view by accessing the superview property to get your parent view.

How can I outsource view-change-operations from my view controller upon accelerations?

I have an view controller class. I want to do some things in my view when the accelerometer detects movement and calls the accelerometer:didAccelerate: method in the delegate object.
That delegate object is the problem here in my brain. Currently, my brain is freezed and I don't get it what would be better. Let me know what you think!
Solution 1)
In my view controller class I conform to the UIAccelerometerDelegate protocol, and implement that accelerometer:didAccelerate: method.
In the -applicationDidFinishLaunching: method of my AppDelegate class I set that view controller object up as the delegate for receiving method calls upon accelerations. I think that's not really good.
Solution 2)
I create a blank new object called AccelerationDelegate, conform to that UIAccelerometerDelegate protocol, implement that accelerometer:didAccelerate: method and in the -applicationDidFinishLaunching: method of my AppDelegate class I set that view controller object up as the delegate for receiving method calls upon accelerations.
But for solution 2 my brain got stuck a little bit! How would I access the view objects from my view controller inside that object?
The problem here is, that I have more than one view controller around. I use a tab bar controller to switch between them.
Any suggestions how I could get that right?
I agree that the second method is better. Are you looking to access just the currently selected tab view, or just a specific view in your app.
In any case, what I would do is to set up properties for your UITabViewController in your UIApplicationDelegate so that you can access it from the delegate (you can get the app delegate by calling [[UIApplication sharedApplication] delegate]). For example:
YourApplicationDelegate *appDelegate = (YourApplicationDelegate *)[[UIApplication sharedApplication] delegate];
FirstUIViewController *firstViewController = appDelegate.firstViewController;
[firstViewController doStuff];
where firstViewController is a property on your delegate.
If your acceleration is specific to one view controller, then it makes sense to have the view controller receive the information necessary to alter its own subviews. However, it might be better to set your view controller to be the delegate when the view appears, and set the delegate to null when it disappears. (Specifically, - (void) viewWillAppear: and - (void) viewWillDisappear:)