How to set the delegate to another ViewController? - iphone

I've recently started developing for the iPhone and so far I'm doing pretty good but there's this basic pattern I really don't seem to get.
Say, I have a TabBar with two views and a custom delegate protocol, thus my structure is the following:
AppDelegate.h/.m
myDelegateProtocol.h
FirstViewController.h/.m
SecondViewController.h/.m
MainView.xib
FirstView.xib
SecondView.xib
Now I want to achieve the following: I placed a button in the FirstView.xib and I'd like the IBAction which it invokes (inside FirstViewController ofc.) to send a message to the SecondViewController ([self.delegate tellSecondViewContrToSayHi]) and invoke another method which simply prints a log into the console saying "hi I'm here."
So far I know what I need to do in theory:
Specify the protocol.
Implement the protocol in the SecondViewController.
Create an id< myDelegateProtocol > delegate inside my FirstViewController,...AND last but not least:
Set the self.delegate = secondViewControllerObject.
Now, nr.4 is where the problem's at. How on earth do I link the delegate to the other viewController? I mean I'm not the one instantiating the views as the tabBar kinda does that for me,... any advise? Or am I just way too tired to notice a really stupid thing I did somewhere?
Theoretically the same question also applies to the target:action: thing,... I mean, how do I define the target?
Thanks a lot,
wasabi

You have the right idea, assuming that you want relatively tight coupling between these controllers via that delegate protocol.
Since neither controller knows about the other until that delegate property is set you need to have some object which has a reference to both of them wire up that relationship. In your case that's probably the application delegate which can create both controllers, set one as the delegate of the other, and pass both along to your tab bar controller.
What you might actually want is to have the app delegate give both controllers a reference to some shared model object. Your FirstViewController can update that model when you tap a button and your SecondViewController can observe changes to the model to update it's display (or just update its view when it appears based on the current model state). That way your controllers don't need to know anything about each other.

Related

How to tell your viewController to update and pass it an object with the update specifics?

I have a general working knowledge of object-oriented programming and I'm currently trying to create an interactive novel program in Objective C using Xcode 4.2 with storyboarding.
I have a storyController class that instantiates the page objects and a viewController class that needs to display the pages. My fundamental question is the best approach for my storyController to tell the viewController to update it's text to display that of the new page object. My instinct tells me that my storyController needs to call a method on the viewController and pass it a page object. However, I'm not sure how to reference the view controller since it seems to be automatically created and linked by the storyboard (I don't see the viewController instantiated in the appDelegate).
I've read this post: http://www.iphonedevsdk.com/forum/iphone-sdk-development/54859-sharing-data-between-view-controllers-other-objects.html and it seems to address the issue. I could make a shared object on my appDelegate and use KVN or NSNotification to tell the view controller to check it.
I've also been reading about delegates and protocols as possible solutions.
So considering these potentially different approaches, which would be best to tell my viewController to update itself and pass it an object that contains the updates?
I would suggest looking at the UIPageViewController class, which probably will do a lot of the heavy lifting work for you, depending on the specifics of your app. This class is essentially a controller class that contains multiple viewControllers. Each managed viewController becomes essentially a "page" in your book.
Here is a tutorial using UIPageViewController.
If this approach won't work for your app, I'd still suggest using a separate viewController instance for each "page" of your book and handle the transition between the pages with segues.

Understanding the MVC design pattern in Cocoa Touch

Usually I just put together an app in any random way, as long as it works, but this means I don't pay any attention to any design patterns. My application currently makes extensive use of global variables (I carry an instance of my AppDelegate in every view controller to access properties I declared in my AppDelegate.h). Although it does what I want it to do, I've read this is not a good design practice.
So I want to start making my code "legit". However, I can't imagine my app right now without global variables. They are so important to the well-being of the app, but this must mean I'm doing something wrong, right? I just can't imagine how else I'd do some things. Take for example this:
You have two view controllers here, a SideViewController and a MainViewController. Using global variables, such as say if the entire application had one shared instance of SideViewController and MainViewController (appDelegate.sideViewController and appDelegate.mainViewController), I can easily communicate between the two view controllers, so that if I press "News Feed" in my SideViewController, I can tell my MainViewController to reload it's view.
I can't imagine, however, how this would be done if these were not global variables? If an event occurs in my SideViewController, how would I notify my MainViewController, in a way that is in accordance with design standards?
I can't imagine, however, how this would be done if these were not
global variables? If an event occurs in my SideViewController, how
would I notify my MainViewController, in a way that is in accordance
with design standards?
The same way you do it now, except that the SideViewController gets its reference to the MainViewController from a different place.
How are these two view controllers created? It's likely that it happens in one of two ways:
One of the objects creates the other. In this case, maybe the MainViewController creates the SideViewController.
Some other object, such as the app delegate or another view controller, creates them both.
In the first case, the MainViewController has a reference to the SideViewController as soon as it creates it. It can store that reference in one of its own instance variables, so that it can always send messages to the SideViewController that it created. Similarly, the MainViewController can give the SideViewController a reference to itself (that is, to the MainViewController), and the SideViewController can store that and use it in the future to talk to its MainViewController.
The second case is similar -- if the app delegate (or some other object) creates both MainViewController and SideViewController, that object knows about both objects and can configure each with a reference to the other.
In both cases, the objects in question are able to communicate with each other just as easily as they ever did and there's no need for a global variable.
What I've explained above is perhaps the simplest way to accomplish what you asked for -- communication between two objects. There are a number of patterns that can be used to refine the relationship between those objects to make your code even better:
delegation: Give SideViewController a delegate property, and define some protocol that establishes what SideViewController expects of its delegate. Implement that protocol in MainViewController. Make your MainViewController instance the SideViewController's delegate. SideViewController doesn't need to know exactly what type its delegate is -- it only cares that it's delegate implements the required protocol. This makes it easy to use SideViewController with something other than MainViewController if that opportunity arises, or to use it in a different project.
notifications: SideViewController may not even need a delegate -- it can simply broadcast notifications about certain events to any object that happens to be listening. This is particularly effective if more than one object might need to know about something that happens in SideViewController, or if the objects that care about SideViewController's actions might change.
MVC: Instead of telling MainViewController that something has changed, SideViewController just changes the data in the model. Whenever the MainViewController's view appears (or any other view controller's view, for that matter), the controller reads the data from the model and redisplays itself.
If you're interested, you might want to pick up a copy of Erik Buck's Cocoa Design Patterns, which explains these patterns and many others in great detail. Don't feel like you have to learn it all at once, or that it's all too much trouble. Learn a little bit at a time and see how it improves (or doesn't) your projects.

The ultimate guide for writing parent view controllers

Short version
If I'm writing my own custom split view controller, what do I need to do to make the child view controllers work as expected?
For instance: to send viewWillAppear: and so on.
Long version
Background
A while back I answered the following question:
Switch between UIViewControllers using UISegmentedControl
With the following answer:
The right way to do it is to have the controller handling the UISegmentedControl add the views of the controllers as subviews.
[self.view addSubview:controller.view];
It's your responsibility to send viewWillAppear: and so on.
However, tc. pointed out that this is not as trivial as it sounds:
No. View controllers are not meant to be used like that - controller will miss out on a lot of the UIViewController magic that's taken for granted (namely -view{Will,Did}{Appear,Disappear}: and -shouldRotateToViewOritentation:).
By "magic", I'm referring to everything UIKit does behind the scenes. You also forgot -parentViewController (which is important for things like modal view controllers). Additionally, somewhere in the depths of UIKit, it automatically calls -viewSomethingSomething: for you, so you might get -viewDidDisappear: twice! (I can't remember the exact details, but there's another user reporting that all you need to do is call -viewWillAppear: and the other three methods happen automatically.) The key issue is that Apple doesn't document the "magic" or how it changes between OS updates.
Since then I've been thinking that one should compile a guide for what needs to be present in the parent view controller in order for the child view controllers to work as expected. Apple's documentation doesn't cover this and Google didn't help much either, so now I'm hoping to find this knowledge within the stackoverflow community.
I've written several controllers like this and they all seem to work, but I can't help but wonder if I'm missing out on important view controller magic.
I think the best solution is "don't do that". Instead of hoping that you can duplicate all of UIViewController's behavior without being rejected for using private API calls why not create non-UIViewController controller objects to manage your subviews? A "controller" is not necessarily a UIViewController.
At a minimum you would need to override or mix in replacement getters for parentViewController, splitViewController, navigationController, tabBarController, and interfaceOrientation (and probably also modalViewController). For each property you would need to make sure that any private setter called by UIKit still works as expected and any changes to those values made by modifying UIViewController ivars directly are also correctly reflected in your implementations.
You're also going to need to figure out how UIKit determines which UIViewController is currently active and should receive view controller lifecycle methods because you need to make sure that these are sent to your container view controller and not only to one of it's children.
You will also have to hope that you haven't just constructed a situation which isn't supported by any of Apple's view controller classes. For example will any of them break if they have a parentViewController but their navigationController, tabBarController, and splitViewController are all nil?
Finally you'll need to keep up with any changes to these private implementation details with every iOS release.

Passing info to a delegate from a UIActionSheet

My understanding about passing data back to a delegate completely revolves around making a new view controller and having it conform to a protocol.
I am trying to get a time input back from a UIDatePicker set with the UIDatePickerModeCountDownTimer mode and I am running into issues.
Inside of Main1.m I create a UIActionSheet *action, and set everything up so that it presents itself with a UIDatePicker on a click. Inside of Main.m I also say:
main.delegate = self;
If this were not a UIActionSheet, I would make a protocol reference inside the new ViewController and then have the new vc pass data to a method that Main has, but I can't do that with a UIActionSheet!!
What am I missing? I assume there is something inherently different about Action Sheets, ut I can't figure it out.
Thanks for your help!!!
EDIT:
Sorry! I was giving a generic name to my view controller. It isn't actually Main.m, its FirstViewController.h/m
I didn't realize that my generic reference was getting mixed up with the Main.m file that is completely different than a vc.
I don't exactly understand why you're putting your delegate assignment in Main.m. I assume that you're setting up your UIActionSheet in a ViewController, and launching it from there. In this case, your ViewController is your delegate object. So you need to make sure that your ViewController implements the UIActionSheetDelegate. ie:
#interface SomeController : UIViewController <UIActionSheetDelegate>
Then you simply implement the required methods of that delegate in your view controller class, and that should do it. If I'm missing something about how you're implementing this, then you need to provide more code examples to examine.

UITableViewController.view crash

So I'm trying to use a UITableViewController (let's call it homeView) in my iPhone application to display a simple table with only a few rows of text which are loaded from an NSArray in the controller. I want to display the table in the grouped style in a subview of a subview (let's call it subSubView) of my main controller. When I try the following: [subSubView addSubview:homeView.view], my app crashes on launch. However, when I allocate the object without adding it to any views, it launches fine.
What's the best way (or rather a working way) to display the table generated by my UITableViewController?
There isn't enough to tell for sure what is going on, but if I had to guess I would think that you probably aren't retaining homeView. Normally I would say that as a comment to your question, since it is not really an answer, but I have a completely separate answer:
Just use a UITableView, not a UITableViewController. Instead of trying to embed a controller within a controller (which is quite difficult since Apple doesn't expose the necessary tools to actually modify the view controller hierarchy), just make the VC you are writing support the appropriate delegate/dataSource methods and directly create the view.
While it might make some logical sense to try to embed VCs inside of each other, with the exception of the builtin container VCs (UINavigationController, UITabBarController) it Really Doesn't Work™. The technical reason for this is that internally some of the event routing and messaging depends on parentViewController being correct, but since you can't set it (setParentViewController: is private) tons of latent bugs in UIKit start rearing their head. The internal collection classes can set the parentViewController correctly, so everything works right.
Also, one last thing. In your question you named your view controller homeView. Please, please on't do that. A view controller and a view are separate things, call it homeViewController. Aside from the fact that a lot of new iPhone developers get confused about what the distinction is, there is nothing more aggravating then tracing through someone else's code and realizing that something you are assuming is one type is another.