How to send actions to targets other than File's Owner? - iphone

I am trying to send actions from button touches to a controller other than the one acting as File's Owner. I have four distinct areas of the screen that I would like managed by four separate controllers (buttonController, toolbarController, textController and graphicController) with a fifth controller (mainController) controlling the other four. mainController is also the one associated with File's Owner. To get button presses to be handled by mainController is easy:
MainController.h file:
- (IBAction)buttonIsTouched:(id)sender;
MainController.m file:
- (IBAction)buttonIsTouched:(id)sender {
..handle button touch event etc.
}
Then in Interface Builder, associate the button Touch Down event with File's Owner and select buttonIsTouched and away you go. All works fine.
However, when I do exactly the same thing for a controller that is not the File's Owner controller the application crashes. Here is what I did:
Create four controllers as instance variables of mainController.
Instantiate them in -[MainController viewDidLoad].
Provide a button handling method exactly as above
In Interface Builder, drag an Object template from Library (four times) onto the mainController.xib browser.
Set the type for these objects to ButtonController, ToolBarController, TextController and GraphicsController respectively.
Associate the Touch Down button event with the ButtonController object and select the buttonIsTouched entry (in the little pop-up box)
Build and run the application
Click the button
Crash - sorry, I didn't write down the error code but it was akin to INVALID_ACCESS
Of course, I can have all the input go first to mainController and then delegate down to each individual controller but this seems an inelegant solution especially since I want the lower controllers to do some work before messaging upstream to mainController. Having to go through mainController and then back kind if irks me.
If anyone knows how to do this and has some sample code that does something similar to what I want to do I would appreciate it.
ac

One problem of your approach is that your instantiate two objects for each of your four sub controllers ButtonController, ToolBarController, TextController and GraphicsController. You’re creating the controllers programmatically in viewDidLoad, but they have already been instantiated from the loaded nib.
You shouldn’t create the controllers in viewDidLoad, but instead use retaining IBOutlet properties in your MainController to wire them up in IB.
Then the controller objects are owned by the MainController and are not removed after nib loading. This will also remove your memory error.

Related

Actionscript style events in Objective-C?

I'm a newbie doing Objective-C, coming from Flex/Actionscript development.
I have an iPhone app with an UIApplicationDelegate conforming delegate called MyAppDelegate - it has a UIWindow.
Instead of adding buttons, labels and whatnot directly to the window, I guess I'm supposed to make a child class of UIViewController for every screen I wanna make. When that screen should be displayed, I add the respective controller's view to the window as a subview. When another screen should be displayed, I should pop off any other view from the window and add the new view. Hope I got everything correct so far...
My intent is to make each view controller only know about its own things, so let's say I wanna call view A from view B, in ActionScript I'd add a button in A firing off an event which would be caught in view A's owning object (could be the application), which could take proper action (remove view A, instantiate view B and display it).
How do I do this in Objective-C?
A UIControl, such as UIButton, can have any number of event listeners registered with:
- (void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents;
The target would be the view controller you want to receive the method, and the action is the method you want called. For a button, events is usually just UIControlEventTouchUpInside. If the target is nil, the event will pass up the responder chain until a responder implements the action. If you pass #selector(buttonClicked:) then the target should have this method:
-(IBAction) buttonClicked:(id)sender;
The sender will be the button that was clicked. IBAction is equivalent to a void return type. You can bind the action in Interface Builder if you prefer that to doing it programmatically.
When another screen should be
displayed, I should pop off any other
view from the window and add the new
view.
This is basically correct, but usually you use a meta view controller like UINavigationController to manage view controllers. Even if you do not use the UI that a meta controller might present, it is convenient to have view switching managed for you.
If you're coming from Actionscript you may be interested in looking at the PureMVC framwork for objective C. Using PureMVC you'll create a combination of Mediators, Commands, and Models for application interaction.
With PureMVC you register notification with the facade, and you define listeners in your mediators to respond to these. This is pretty close to the event model you're used to in Actionscript. (At my last job we added some categories to the UIResponder to remove some of the cruft in doing this). If you're creating a considerably sized application, then I would recommend you give it a look; it certainly helped us keep everything manageable.
If you don't want to pull in a third party library then you should define your view manipulation code in your MyAppDelegate and use the [UIApplication sharedApplication] class method to access the globally shared instance.

View or ViewController... both are views?

In Interface Builder... when I drag a VIEW into the document window... and then double-click on it... it opens and displays the VIEW window. (As expected)
... but when I drag a VIEW CONTROLLER into the document window... and then double-click on it... it also opens and displays the VIEW window, too. (It says "view" right on it.)
Is that right? (Or am I totally misunderstanding things?)
I thought a VIEW was the actual object to draw/drag things into. No?
I thought a VIEW CONTROLLER was just the CODE for your view. No?
I dont have experience with Interface Builder, but a a controller object provides the custom logic needed to bridge the application’s data to the views. In iPhone applications, a view controller is a specific type of controller object that you use to present and manage the views of your application.
Each ViewController has a View property associated to it, which is the one you are seeing in interface builder.
The view stored in this property represents the root view for the view controller’s view hierarchy. Whenever you present the view controller on screen (either modally or as part of view controller–based interface), this view is retrieved and displayed in the application window. The default value of this property is nil
Each view controller object is the sole owner of its view. You must not associate the same view object with multiple view controller objects.
Apple wants you to follow the Model-View-Controller system when you develop apps, and it's pretty straightforward and logical.
The Model is the core of your app. It processes databases, network connections and whatever you need. It's basically custom classes you create in Xcode.
The View is the interface between your app and your users. You can create them in Interface Builder and put them in NIB files (preferably one view every file).
The Controller is the glue between your view and your model. It defines the behavior and state of views (button enabled, label content, etc.) based on what it gets from the model (like a database). It also performs actions on the model based on the events it receives from the views it managers (controls), like changing records in a database or changing variables in objects.
The idea behind all this is that the model can be used in every platform with minimal modification.
Every controller should be linked to one view, and one view only, like a table (UITableViewController) or a screen's view (UIViewController). You subclass the controller you want and you add it to the NIB of the view it's associated with. That's how you do it:
In the NIB, select the File's Owner.
In the Identity inspector, set the class to the view controller you created in your project.
In your custom controller class, create IBOutlets for every view (such as deleteButton) in the associated view you need to have access to. Create and synthesize the properties for every outlet.
Create IBActions for every event you want to register (such as addButtonClicked).
In the NIB, drag a line with your secondary mouse button from the File's Owner to the wanted outlet view and then select which connection you want to make. Repeat until all your IBOutlets are properly connected.
Drag a line with your secondary mouse button from the view you want to register events from to the File's Owner and then select which connection you want to make. Repeat until all your IBActions are properly connected.
Write your code for the controller.

Loading a View from IB

This should be a pretty easy fix, but I haven't figured out from reading the Apple documentation or other SO questions how to do this simple switch from creating my Interface programmatically to using Interface Builder.
I am basing my code around this framework:
http://www.pushplay.net/blog_detail.php?id=27
The only difference is that, where each View is programmatically created (View01.m, View02.m) in ViewDidLoad, I instead want to import from a nib (while still using this framework) for each view (each view has a unique IB design).
Thanks for the help.
Think of IB as an Object Creator and not a code generator. That really helps. What IB does is actually create instances of objects as they are dragged on to the desktop/view/XIB window. It then allows you to start creating various connections (with a control drag on the mouse) from one object to another object. You then instantiate the entire XIB by unarchiving it from your bundle. This is highly automatic and reading up on UIViewController should move you along a bit. Look at:
initWithNibName:bundle:
You basically have two types of connections:
Outlet: This is how you teach one object about the existence of another object. For example, you might have a controller object that needs access to a button. You create an outlet (either in XCode Text Edit in the controller.h file/property area or in IB by adding an outlet) in your controller and then control-click-drag from the outlet to the button.
Actions: This is how you trigger an event on one object to call a method on another object. Actions will have a prototype of:
- (IBAction) someMethod:(id) sender;
I think the ":(id) sender" is optional if your method does not need a link to the object causing the event.
Within IB, you can arrange objects and set various attributes like size, color, position, target/actions, user interactions, Files Owner...
That brings me to files owner. Big concept here. It tends to be the Controller that loads the NIB (OK: I have a custom window controller I have used for over 15 years but Apple has a really good one UIViewController that does all sorts of goodness.) and acts as a proxy in IB. It is not actually instantiated in IB but it will be when you alloc and request it to load the NIB (XIB files are XML files that are turned into NIB files by the compile process)

Interface Builder. How do I Replace Programatic ViewControllers with IB ViewControllers?

As I move more and more of my UI construction from XCode to IB there is one remaining hurdle I need to clear.
Currently, I create my custom viewControllers in XCode with custom initializers. They typically have a bunch of ivars that are set during initialization via passed params. I would really like to move as much of this as I can to IB.
Could someone carefully explain to me - or, better, point me to code - how to replicate the XCode approach of passing params via custom initializer in IB - presumably via IBOutlet.
Thanks in advance.
Cheers,
Doug
UPDATE:
A typical scenerio is my AppDelegate will go out to the network, grab some data - NSMutableData, and then pass a reference to a root viewController - typically a TableViewController - that is pushed on the viewController stack of an navigationController.
When a cell is selected an secondViewController is alloc/init'ed and a subset of the data is passed to it. The secondViewController goes to the network for a bit more data, builds a view hierarchy, hands bits of the retrieved data to each subview and then each subview is messaged with setNeedsDisplay to process the data.
It is this hierarchical data passing that I want to hand off to IB is possible.
You can still have a custom initializer. However, inside this initialized you'll call -[initWithNibName:#"yourNibName" bundle:bundle]. You will connect your UIKit related instance variables (UILabel, UIButton, etc.) in Interface builder and therefore won't have to instantiate those objects in your initializer. Other instance variables (strings, dictionaries, custom objects, etc.) you'll instantiate and set in your initializer.
Also, keep in mind that manipulation of your UIKit related variables such as setting label text, or setting the position of a UIView, should be done in the viewDidLoad method since these objects may not have been fully created at the time initializer is executing.
Let me know if you need more information.
Lets say we have MyViewController which extends UIViewController and MyViewControllerParameter which is one of our custom objects we pass into MyViewController on initialization. MyViewController has an IBOutlets of type MyViewControllerParameter defined in it. We want to put MyViewController into another containing view, ParentView.xib. ParentView.xib is owned by ParentViewController.m and created elsewhere. We double click ParentView.xib in XCode to launch Interface Builder. We then drag an "object" from the Library View (I believe you open the library view with Cmd+Shift+L if its not open by default) onto the Document window. (This is the window activated by Cmd+0.)
An "Object" is an arbitrary widget found in the library (identified by a solid gold cube icon) that can represent anything in your project. After we've placed the new object into the document window, we type Cmd+4 to open the Indentity Inspector Window. In the top text field we can type the name of our view controller parameter, "MyViewControllerParameter". Next we find the ViewController widget in the library and drop it into the document window. We open the Attribute inspector (Cmd+1) and set the nib name to "MyView.xib". This should cause CocoaTouch to load MyViewController using the definitions in "MyView.xib".
Open the identity inspector and set the class attribute to MyViewController. Last we right click or control click (or two finger tap if we've correctly configured the trackpad on our MacbookPro) MyViewController in the document window. That will open a window showing all of the IBOutlets in MyViewController. Drag a line from the little open circle next to the MyViewControllerParameter outlet and drop it on the MyViewControllerParameter object sitting in the document window. Connecting these dots is how you set parameters between objects using interface builder.

Using default tab-controller created project... first view controller doesn't respond to addsubview

I created a project using the default tab-controller project. I am using interface builder to edit the .xib file and add images and buttons. I hook them up to the FirstViewController object in interface builder (that I created and set it's class to the same as the code file). I hooked everything up using IBoutlets and IBActions.
Everything was working fine, but then I made some changes in interface builder (added a UILabel) and now a method that is run when clicked (I ran through it with the debugger) has a line that adds a subview to the view controller, and it acts as if it wasn't executed. The method (and code is run through) is executed with no errors (per the debugger) but the view is simply not being added. This happened after I made some change via interface builder.
Now, if I hook-up my button to "Selected First View Controller" by clicking on the appropriate tab and dragging the IBOutlet to the UILabel, that label now has multiple referencing outlets. Now, if I do the same thing for the button, the method (the IBAction) is executed twice but the subview is actually added and displayed. But, I get a memory access error because my IBAction (button) method access a property that stores something. I am guessing this has to do with somehow creating the memory in the First View Controller but trying to access it in the Selected First View Controller? If that makes any sense?
I have no idea why this is happening and why it just the button suddenly stopped working. I tried to explain this problem the best I could, it is sort of confusing. But if anyone has any tips or ideas I would love to hear what you guys think about this problem and how to solve it.
Are you sure the first outlet is actually hooked up. If you name an outlet such that it conflicts with some other property that is set while the nib is loading (via initWithCoder:) it can cause things to not end up being hooked up properly. You can check that by NSLog'ing out the value of the outlets in your awakeFromNib.
It also sounds - and feel free to correct me if I'm mistaken - that you're attaching actions in the view loaded by the tab bar to the tab bar's controller. The two entities are quite different and any data that you wish to access from the view should be referenced from the view's controller rather than the tab bar's controller (which should have a fairly light-weight job in loading and unloading your other view controllers). Similarly, you should not be adding a subview to the view controller, it has no idea about what to do with a subview - you should be using the view controller to add a subview to your view. While it seems like a matter of semantics a view controller is fundamentally different from a view. The former has the job of managing the contents and behaviors of a view and to respond to the view's actions where necessarily while the latter is simply a mechanism for displaying thing son the screen.