Getting another controllers instance class - iphone

I have learned how to pass and instance of a class to another class, but I have yet to understand how to do it the other way arround.
Example:
Lets say that we are in rootViewController and in the didSelectRowAtIndexPath we pass the instance of playlist to someviewcontroller like this:
SomeViewController *someViewController = [[SomeViewController alloc] initWithNibName:nil bundle:nil];
playlist = [arr objectAtIndex:indexPath.row];
someViewController.playlist = playlist;
Now this works perfectly for passing the class instance forward, but lets instead say that Im in the someViewController and want rootViewController playlist.
How would I go about achieving this using a similar technique? Thanks for helping!

You should check out the delegation pattern: https://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/Delegation.html
Also check out Apple's CoreDataRecipes sample code:
http://developer.apple.com/library/ios/#samplecode/iPhoneCoreDataRecipes/Listings/ReadMe_txt.html
You can see in the sample code that the RecipeListTableViewController is a delegate of the RecipeAddViewController so that when the RecipeAddViewController is done RecipeListTableViewController can show the recipe in the RecipeDetailViewController.
Delegation also helps with your object graph because the reference to the delegate is a weak link which means that it won't retain your delegate. So by using the delegation pattern you ensure that you don't create any retain cycles and ARC will take care of memory management for you.

Related

iPhone Programming - Passing NSMutableArray between view controllers

I have a RootViewController and a DetailViewController where I am trying to pass a NSMutableArray from the rootView to the detailView. I have created an object of the RootViewController inside the DetailViewController and accessing it like the following
RootViewController *root = [[RootViewController alloc] initWithNibName:#"RootViewController" bundle:nil];
detailViewArray = [root.rootViewArray copy];
Note: Both the arrays are declared in the .h files; synthesized and then allocated and initialized array = [[NSMutableArray alloc] init];
Problem: I am not too sure why it still doesn't work. I have tried a lot of the solutions on the internet but it didn't quite work for me. The property for the root array is nonatomic, retain Is it something wrong with that? Do I need to change it to something or the method I am following is just not right.. Please if someone could help!
If you alloc/init the RootViewController inside the DetailViewController, you are creating another instance of the RootViewController. You are not getting the same instance (with data) of the rootViewController.
That said, even passing a reference of a viewController to another viewController to then poke at it's data is sort of bad. It creates tight coupling between the views.
Instead, if you need to get data, consider using a delegate to communicate between the views.
What exactly does delegate do in xcode ios project?
Tutorial:
http://www.theappcodeblog.com/2011/04/15/passing-data-between-views-tutorial-using-a-protocol-delegate-in-your-iphone-app/
Another option is to create a shared model (read up on model view controller patterns). It's typical in that pattern to create a model and share data by getting a singleton instance of your model:
MyModel *model = [MyModel sharedInstance];
Then, each view can set and read data from that same (singleton) instance of the model.
Which to choose? The model is better if many views share the same data. A delegate is appropriate for a couple views to communicate with each via callbacks.
Even there is an another way to pass array.
MyViewController mvc = [[MYViewController alloc] initWithNibName:#"MYViewController" bundle:[NSBundle mainBundler]];
[mvc getDataArray:<pass your array>];
[self.navigationController pushViewController:mvc animated:YES];
Here it will set a dataArray property first then it will push the view controller to MyViewController class.
This will be useful when you are very much concerning about memory. Because if you are using static (sharedInstance) then its scope persistance within the entire application scope.

Calling methods on a certain UIViewController while another viewController is on the screen?

I have a regular app with a bunch of view controllers - let's say viewControllerA, viewControllerB, and viewControllerWeb.
"viewControllerWeb" is a UIWebViewDelegate and just has a UIWebView in it. I would like to call methods on it even when I am interacting with viewControllerA or viewCOntrollerB. (methods like [webview stringByEvaluatingJavaScriptFromString:])
So essentially, it seems like I want "viewControllerWeb" to be open in the background somehow. Anyone know how to accomplish something like this? Am I thinking about this the wrong way?
Hope you guys can help me out! Thanks a lot!
Well, if you want to call these methods from the VC where the WebViewController was created, you could easily do that by making webViewController a global variable.
If you want to access it from an outside VC, then you could make WebViewController a singleton that you can access from anywhere inside your app. Something like this would do:
static WebViewController *sharedController = nil;
+ (WebViewController*)sharedController {
if(sharedController == nil)
sharedController = [[WebViewController alloc] initWithNibName:#"WebViewController" bundle:[NSBundle mainBundle]];
return sharedController;
}
That would allow you to access your WebViewController from anywhere:
[[WebViewController sharedController] stringByEvaluatingJavaScript...]
EDIT: As RyanR pointed out in the comment, this obviously will keep your view controller retained until you explicitely release it. Common mistake with singletons is to forget about them and never release them — make sure you do!
You shouldn't be retaining a copy of your view controller. In case you want functionality to be shared, abstract it out to a singleton.

several questions about obj-c (delegate, viewController, simulator)

i had several questions about objective-c, could you help me understand it? Here they are :
why do we use sometimes :
self.myView.delegate = self; ?
There is the same thing with the Ipad and Splitview : why do we use the delegate explicitly like this :
uisplitviewController *svc = ...
svc.delegate = pvc.otherViewController;
For now, i understand the "appDelegate", but what about this delegate method?
2.I saw several times the use of another "viewController" as below, instead of "allocating" directly the "mainViewControler", why do we use this intermediate?
MainViewController *viewController = [[MainViewController alloc] initWithNibName:#"MainView" bundle:nil];
self.mainViewController = viewController;
[viewController release];
3.Following some tutorials from appsmuck.com, i saw that they use :
"(void)loadFlipsideViewController "
but no "loadView", so, can we replace the "View" with the controller view?
4.Finally, i can switch from "iphone" Simulator to "ipad" simulator, but i always get the ipad simulator each time i build the projet, is there a way to always let the "iphone" simulator to be the simulator by default?
And that's it. :) Thanks for your help
Paul
1 . Delegation Design Pattern
The delegation design pattern is a way
of modifying complex objects without
subclassing them. Instead of
subclassing, you use the complex
object as is and put any custom code
for modifying the behavior of that
object inside a separate object, which
is referred to as the delegate object.
At predefined times, the complex
object then calls the methods of the
delegate object to give it a chance to
run its custom code.
2 . The reason for creating another viewController is for memory management. It can be done in one line but then you will be needlessly adding an object to the autorelease pool.
//Now the autorelease pool has to track this object
self.mainViewController = [[[MainViewController alloc] initWithNibName:#"MainView" bundle:nil] autorelease];
3 . -(void)loadView is inherited from UIViewController so no you can not just change it to loadViewController because that will just create a custom method. That is exactly what (void)loadFlipsideViewController is trying to accomplish and by the name of it it should load a new view controller and display it using a flip animation.
4 . In XCode 4 you need to set the scheme to the correct simulator.
First of all, it's going to me MUCH easier to get answers if you break all these queries up into separate questions. You're going to have a hard time picking an answer that answers them all best.
That said, I'll give #1 a shot.
Many types of objects in Cocoa (and Cocoa Touch) send messages. Some, like NSFetchedResultsController send messages when their contents change. Some, like UITableViewController, send a message when a table cell is touched. These messages have to GO somewhere. They can't be sent just "out there" or nothing will ever hear them. These messages need a destination. In Cocoa, the destination for these messages is called the "delegate." As in, "I designate this object to be my delegate, and receive my messages."
If you are in a viewController that is controlling a UITableView, very often is makes sense to simply specify "self" as the delegate. That is saying, in effect, hey, Mr. UITableView, just send your messages to me, I'll handle them. In turn, your viewController must declare (in the .h) that they conform to the UITableViewDelegate protocol, and then the required methods in that protocol must be implemented in your .m.
This is a VERY common pattern in Cocoa. Don't proceed until your understand it.
For delegate you can check use of Delegate in iphone sdk. I have a long answer there.
#property (nonatomic, retain) MyClass *obj;
MyClass *tmpObj = [[MyClass alloc] init];
self.obj = tmpObj;
[tmpObj release];
Let's see what happens here. [MyClass alloc] allocates an object and you have retain count of 1. In the next line you are calling the setter which also retains. So you are increasing the retain count. Naturally you will release this property later, may be in dealloc. So now you are owner of this object 2 times. To match the first ownership via alloc you release in the 3rd line.
Now see what happens if you do this in one line:
self.obj = [[MyClass alloc] init];
You have gained ownership here two times, one through alloc and one through retain property. So you need to write [obj release] twice. Otherwise you will have a memory leak. But releasing twice is confusing and easier to create bug and you should never do this. Or you need to autorelease this in the same line which is also confusing.
So the summary is if you have retain property, then write three lines of code to handle with double ownership.
Sorry, can't help with other parts.

Adding a variable to all UIViewControllers

I have this class (let's call it FooViewController) that's a subclass of UIViewController. It's supposed to act similarly to a UINavigationController, in that there's a rootController and you can add other UIViewControllers to it. And each UIViewController within FooViewController can create another UIViewController and push that new UIViewController to FooViewController.
Here's an example of what I mean. This is the auto-created code when you add a new UITableViewController to your project.
DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:#"Nib name" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
Here's the question: How can I have a variable like navigationController in all of my UIViewControllers? Just like you can add methods to existing classes using categories, can you also add variables to existing classes? It sounds like Associative References is what I'm looking for, but it's only available on a Mac.
One solution that would work for me is to subclass all the UIViewControllers that I might use, and have my actual classes subclass off of those. For example, I might do:
#interface FooUIViewController : UIViewController {
FooViewController *fooViewController;
}
#interface FooUITableViewController : UITableViewController {
FooViewController *fooViewController;
}
So that I could do: [self.fooViewController pushViewController:detailViewController];. But this seems like a dirty way of doing it.
I feel this shouldn't be a difficult thing to do and maybe I'm thinking about it wrong. Any thoughts? Thanks!
No, you can't add an instance variable to UIViewController.
Subclassing is not a bad way of doing it, considering more things it can do in relation to FooViewController. It doesn't harm the design of your classes because they depend on FooViewController anyway (just as the same as UIViewController having navigationController property).
Another way of doing it is to access the FooViewController object via the application delegate. But I think this is a dirty way of doing it (because they now depend on your application delegate).

iPhone: Using a Singleton with Tabview Controller and Navigation Controller

I have developed a small iPhone application by using singleton that I use to navigate through the views. Here is a sample method from my singleton class.
+ (void) loadMenuController:(NSMutableArray *)menuItems{
MenuViewController *menuViewControler = [[MenuViewController alloc] initWithNibName:#"MenuViewController" bundle:nil];
[menuViewControler setMenuItems:menuItems];
RootViewController *root = (
P2MAppDelegate *appDelegate = (P2MAppDelegate*) [[UIApplication sharedApplication] delegate];
UINavigationController *navController = [appDelegate navigationController];
[navController pushViewController:menuViewControler animated:YES];
[menuViewControler release];
}
Now my requirement has changed to require a tab view controller . I could change my application delegate to a tabview controller but I still need to navigate inside each tab. I am unable get a clue how to navigate from my singleton class.
Please guide me. Please let me know if my query is not clear.
Thanks in advance.
Regards,
Malleswar
You shouldn't be using a singleton to manage the interface and even if you did, you wouldn't put the UI logic in a class method. You need to rethink your design from scratch.
The normal pattern is to hold the navigation controller or the tabbar controller as an attribute of the application delegate. The app delegate itself should not be a subclass of any controller but just a NSObject subclass that implements the application delegate protocol.
Look at the Apple supplied template projects in Xcode to see the quick and dirty way to structure apps built around navigation and/or tabs.
Singletons should only be used when you have to ensure that one and only one instance of class is alive at one time. You don't need to make your own singleton to manage the UI. The application delegate is attached to the application object which is itself a singleton. This means the app delegate provides all the restriction on class for the UI you might need. You don't need another singleton in addition to that.
Overuse of singletons is dangerous and can cause your design to get trapped in a dead end resulting in a massive rewrite. Think carefully before employing them.