Actionscript style events in Objective-C? - iphone

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.

Related

facebook type left slide bar layout should appear in all view controllers

I have implemented Facebook type left Slide Bar layout in my first view of iphone app. Now, I want to implement this throughout all view controllers (screens) in application, means irrespective of the view the left slide bar should appear on clicking the menu button at the top in all views.
My app contains 25-30 viewcontrollers and my slide bar layout should appear in all views..
Can anyone suggest, how can I include above FB Layout in all views
Thanks in advance
Ramu
Simple, The one view controller in which you have implemented the FB layout and is working. Make it the base class on top of UIViewController. And as for the rest of all the ViewControllers, inherit them from the MasterClass that you just created. Doing this will make the swipe gesture that brings forth the slide bar available to all of your 30 view controllers.
EDIT
Lets see, we have UIViewController, now first of all you create a UIViewController's subclass: say FBViewController ..In this FBViewController you implement the FBLayout such that the swipe and all is working ..on it ..test your app first using only this FBViewController as rootViewController and check all the functionalities.Once everything is working fine, grow on it. What I mean is this.
Say you are creating a Tabbed application, where all the three tabs are supposed to have the same FBLayout style. Then do these steps.
Create FBViewController, it inherits from UIViewController (using UIViewController subclass template, also check the generate XIB button) also have an XIB for it FBViewController.XIB (fully implement FBLayout in it. This will be your base class)
Then Create three more ViewController classes (FirstViewController, SecondViewController, ThirdViewController) again from the UIViewController subclass template, but for these three dont check the generate XIB button. these three will use the XIB of the base class FBViewController (If you are wondering how, then go to step 3 :))
Go to header file of FirstViewController class you created, there you can see #interface FirstViewController: UIViewController replace it with #interface FirstViewController: FBViewController, but before it import FBViewController.h to the header file. Repeat the same for the Other two classes- SecondViewController, ThirdViewController. Since these three will inherit from FBViewController. In their viewDidLoad [super viewDidLoad] will load FBViewController and generate the view. after [super viewDidLoad]; line you can implement ur own methods.
In the three classes just change the initWithNibName method to change the tab bar name and title.
In appDelegate go to didFinishLaunching method and put these three view controller in a tabBarController, set the tabBarController as rootViewController.
And we are done. If your FBViewController is working fine. You will see that all the three classes behave the same way. Thanx to the power of Inheritance.
Cheers, play a bit, have fun.
I had the same problem. I was using a facebook-style menu, and needed it in all view controllers.
You can use a Container Controller. A Container Controller can have the base layout, which I defined in a nib, containing a navigation bar and a bar button item to toggle the menu, and then add child view controllers and remove them as you need them. That way, you can throw whatever view controller you need to the container controller and it will display it.
You can also add gesture control to slide open/close the menu easily.
You will have to make the Container controller your self, it is not standard. I think it is better solution than inheritance, since if you use inheritance you can't make a for example UITableViewController, all your controllers will be of the type of yuor master class. Of course, you can fix this anyway with delegates.
It may sound a bit tricky, but see this tutorial which I used: http://www.cocoanetics.com/2012/04/containing-viewcontrollers/
It wasn't accutally that hard.
EDIT: You can just use a UINavigationController as well. Just set the base view controller to the view controller you want to display, and you can prevent it adding the back button etc to the nav bar by overriding the default methods. Make a UINavigationController as rootNavigationController. Might be simpler.
I'd highly recommend using an open source solution that handles all the edge cases for you - it's both the easiest, most robust and most maintainable (since the community will keep it up to date fro you). ViewDeck seems to be the most popular solution though I have also had success with PPRevealSideViewController. They both provide a very robust implementation that would take a long time to do yourself (e.g. you can optionally enable swipe on the navigation bar or even content area to open the menu). Furthermore they separate the sliding logic and the revealed menu (which can be any view controller you like, but most likely a table view controller) out of your other view controllers. That way any viewcontroller can have a side menu without duplicating any code - separation of concern is great :)
You can make a SharedInstance for SideView class. I am doing same thing for iAD to show throught-out the application.
Please see the the link of iAdSuite ,In which the BannerViewController is SharedInstance so they are easily used for all View Controller
http://developer.apple.com/library/ios/#samplecode/iAdSuite/Listings/TabbedBanner_TabbedBanner_BannerViewController_m.html

viewWillAppear not getting called for detailView of UISplitViewController

I am experimenting with the splitViewController, introduced for iPads and am stuck at a point. I have a button on my detail view of the splitViewController, clicking on which a modal view opens. Now I want to change the positoning of UI controls on the detail view when the modal view gets dissmissed.
A pretty obvious way of doing this would be to catch the view transition in the ViewWillAppear method of the detailView. But it's not being called in this case. I remember facing the same problem in tabBarController where [tabBarController viewWillAppear:animated] was needed to be set before viewWillAppear of views in each tab item got called. I tried doing this with the splitViewController as well, but this doesn't seem to to work.
Any Ideas??
If the positioning is required due to an action that occurred in the modal view, you should use an explicit delegate callback. That will allow you to clearly specify the control flow and resulting behaviour of your app.
You should then define a protocol that has specific methods that carry pertinent information about the action taken. When the action occurs in the modal, perform the protocol method on the delegate, and it can react to that event (for you it seems to be a re-layout of button positioning).
To get an idea of the methods that are abstract enough to handle generic modal behaviour, look at UIAlertViewDelegate protocol. Here the delegate will get an alertViewCancel: message when the user decides to take no action, or alertView:didDismissWithButtonIndex: when they selected one of the options presented to them.
That is a good start for how to define the protocol.
If you need a many view controllers to react to the action taken in the modal, say a Sign In modal, then a better mechanism is notifications.

Custom Wrapper Class for UIDatePickerView on iPhone

I'll be using a datePicker several places in my app. I don't want to "clutter" up each and every viewController with the delegation methods for the UIPickerViewDelegate and UIPickerDatasource, plus I would be doing the same delegation methods over and over again.
Every time the datePicker is in play it's sole purpose is to slide halfway up the screen, let the user select a date and then disappear again.
I was contemplating a wrapper viewController (DatePickerViewController) that implemented the datePicker delegate methods, then did a NSNotification with the value which the user selected, which again was caught by the viewController instantiating the DatePickerViewController.
This would make me a decoupled datePicker and let the viewController instantiating the datePicker know nothing of a DatePickerDelegate, but just know that there would potentially come a notification containing an NSDate. Seems rational to me, like something I would do in other languages. But please correct me if Im digging myself a hole here:)
As I started breaking this down I ran into some difficulties, Im not very experienced in Objective C and Cocoa.
I can build a viewController that in it's viewDidLoad presents a datePicker, running just this will result in a blank white screen with a datePicker in the bottom half of the screen. If I use "presentModalViewController" from the viewController that instantiates the (custom) DatePickerViewController, it of course slides up and covers the whole screen. I would like for the user to still have half the view visible. Much like setting the time in an event in the iCal app. (except they push a new viewController onto the stack). Ahh just realized that what I mean is exactly like the keyboard when it slides in and covers half the screen.
So I guess my main problem is: can you build a viewController that behaves like the keyboard when added to a view. But do all this in the ViewController that is added instead of in the controller instantiating the view.
Hope it makes sense:)
Thank you
(1) Put the picker in a model (edit: should be modal) view. This is how the keyboard is implemented.
(2) The picker controller/delegate should only control the model view and the picker.
(3) In the delegate create two properties such as:
id *target;
Selector theSelector;
and a method like:
-(void) sendPickerResultsTo:(id) theTarget forSelector:(SEl) theSelector;
(4) Before displaying the picker model view, set the target to the calling controller and the theSelector to a method in the calling controller. You can configure the selector method to pass an arbitrary amount of data. It would look something like:
-(void) pickerResults:(NSArray *) pickerResults; //could pass any value as long as it's an object
[Note: this is kind of thing you define a protocol for if you use it a lot]
(5) When you have the picker value just have the picker delegate call:
[self.target performSelector:theSelector withObject:anArrayOfPickerResults];
(5) Add the appropriate method to any controller that needs to evoke the model picker view and set the controller as the target before you display the model picker view.
This will give you a self-contained model picker view that you can attach to any view and which can send its results to any arbitrary object that implements a method with the right signature i.e. implements the protocol.
This is basically a do it yourself version of UIControls addTarget:action:forControlEvents:

Reusing UIViewController for modal and non-modal situations

I have a UIViewController — let's call it "FormController" — which is simply a form that edits an object. I want to use it in 2 different situations:
Creating a new object — using using UINavigationController's presentModalViewController: method.
Editing an existing object — push the view controller onto the UINavigationController stack, not using a dialog method.
There is a slight difference in that in the modal situation I would like to have a toolbar with "Cancel" and "Done" buttons, whereas in the stack situation I would like to just have the navigation bar provided by the UINavigationController.
This would be similar to the Contacts application where the "New Contact" and the "Edit Contact" screens seem to use the same view controller, but the New Contact form is presented modally while the Edit screen is pushed onto the navigation stack.
My question is: What is the best way to handle both situations without having to write 2 separate, but mostly identical view controllers?
I thought about creating a "ModalFormController" which encapsulates the bare "FormController" through composition and adds a toolbar, but I read somewhere in the docs that Apple doesn't recommend nesting view controllers.
Why not use subclassing? Make ModalCreateFormController a subclass of EditFormController and handle the modal-specific stuff in the subclass.
What I do (sometimes) is set up an enum that specifies the type of the view controller.
For example, you might have two types: an Edit type, and an Add ("new") type.
The Add type is implemented via a modal view controller, while the Edit type is pushed onto an existing navigation stack.
In the view controller's -viewDidLoad: method, I simply do a switch/case tree that sets up the title and other appearance characteristics depending on the type enumeration specified above.
The nice thing about this is that it is easy to add a new type. The downside is that the conditional tree for handing this enumeration can get complicated quickly, depending on how different the types are.
But the switch/case tree makes it a lot easier to manage.
So, it depends on what you're trying to do with the two types. But it's definitely doable.
In addition to having an explicit property on the view controller (as Alex Reynolds suggests), two other approaches that occur to me are:
If you have some kind of model object that you're editing, ask it for its current state. If it's ever been saved, then you're in edit mode. Otherwise, you're in create mode.
Look at the value of the controller's parentViewController property. If it's an instance of UINavigationController, then you're in the navigation stack. If you're being displayed modally, it'll be an instance of your list controller.
Ug, I hate extra ivars…
I use this instead:
if([[self.navigationController viewControllers] objectAtIndex:0] == self){
//Modal
}else{
//Pushed
}
It is a bit of a hack, but we are using the logic that if the offending view controller is the first in the stack, you can't go back. Actually we are ignoring the fact of whether it is modally displayed at all.
I had to do this a bunch of times in my app and after trying a couple different ways of doing it, including modal subclasses & a re-usable modal helper classes that used forwardInvocation. I found the best pattern was to make a containingModalViewController method each view controllers that (usually) creates and returns a UINavigationController for the caller to use with presentModalViewController.
In most cases this method builds and returns a UINavigationController with self as the root view controller (with repeated calls to the method checking self.navigationController and returning that instead if it's not nil). Other cases I made a dummy root controller first and pushed self on second in order to get a back button. Then a trick can be used to catch the back button press: http://smallduck.wordpress.com/2010/10/05/intercepting-uinavigationcontroller/
In some cases the view doesn't need a navigation bar and so this method just adjusts some flags and returns self. I even found in some cases that did need a navigation bar it was simpler to make that method invoke self.view, then tweak the view hierarchy to add a UINavigationBar and again return self. But in any case, the setup often isolated to that one method, and the caller handles it the same in each case.
Apple explains how the contacts application works under the hood:
To allow a custom view controller class to be used to both display and edit content, override the setEditing:animated: method.
You get some functionality for free, e.g. Edit/Done button.

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

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.