NavigationController and Modal Views - iphone

I am a newbie to iOS world and have started building custom code on top of a templated code.
So excuse me for the obvious.
The View chain starts with a MainWindow.xib which contains a App Delegate Object, a Window Object and Application ViewController. I dont understand why those objects are needed over there. But what I understand, I need to mention starting ViewController in the "Nib Name" Property to initiate my custom View Controller (called "EmptyViewController"). Its a dummy view controller, just there to avoid crash to happen as a result of missing valid viewcontroller.
I initiate a separate Modal View Controller(MainViewController) inside didFinishLaunchingWithOptions.
Code for initiating modal View Controller --
self.window.rootViewController = self.viewController;
mainView = [[MainViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
// present the viewcontroller
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:mainView];
[self.viewController presentModalViewController:navController animated:NO];
// release it, because it's retained as modalViewController
[navController release];
I do not put this MainViewController inside MainWindow.xib as I want to have navigation at the root of MainViewController.
Inside MainViewController, I push HelpViewController when "help" button is pressed.
But HelpViewController does not show any navigation bar. I do not understand why?
Code for Pushing Navigation bar --
HelpViewController *helpVC = [[HelpViewController alloc] init];
[self.navigationController pushViewController:helpVC animated:YES];
[helpVC release];
So I would like to understand --
1) Why is MainWindow.xib needed? Can I remove it? (Note: I tried to remove it, but then I get blank screen)
1.a) Why are all the controls/objects App Delegate Object, a Window Object and Application ViewController objects needed?
2) Why doesnt HelpViewController show Navigation bar?
3) Another thing I noticed, if I say self.presentingViewController, EmptyViewController handle is returned while popViewController returns me back to MainViewController.
Thanks

The App Delegate simply implements some app-level 'callbacks' by which iOS communicates with your own code. In main.m you can see how iOS is told which of your classes implements UIApplicationDelegate. iOS creates an instance of this class and call these delegate methods ('callback') whenever appropriate (e.g. when the app goes to background).
The Window is something iOS provides, your app needs to tell what to display on it. And, as you saw, this is usually done in didFinishLaunchingWithOptions (which is called by iOS to inform your app things are ready to get started).
A View Controller is a class that handles states of stuff you show on the Window. You don't show stuff directly on the Window, but instead use Views. Every View Controller has a View with UI elements.
The XIB or NIB is a UI description/layout file. A XIB and View are linked together; you need to tell the XIB to which View Controller member (e.g. a UILabel) a UI element belongs, and you tell the XIB which View Controller method to call on a certain UI event (e.g. user taps on a button).
These are the basics. I'm aware it does not answer all your questions; I suggest you read the very good Apple documentation. Don't try to understand everything immediately as things, as you're experiencing, indeed can seem illogical at start.

Related

How Can I Use Buttons to Control Navigation in Root View, Instead of Table Cells?

I'm developing my first iPhone app, which is a navigation based one. I want to know how I can use icons/buttons like this app in the root view to control navigation instead of the default table cell view.
I would appreciate some step by step guide since I'm sort of newbie and didn't get how to do this, reading the documentations, or similar questions.
Rather than starting with a Navigation based app, start with a window based app and create an instance of UINavigationController in app delegate (in method appDidFinishLaunching) and set any UIviewController as it's root view controller. You can then do whatever customizations in that view controller
Finally set the navigationController as rootviewController of you application window.
UIViewController *myCustomRootViewController = [[UIViewController alloc] init];
UINavigationController *myNavController = [[UINavigationController alloc] initWithRootViewController:myCustomRootViewController];
[myCustomRootViewController release];
self.window.rootViewController = myNavController;
[myNavController release];
You really need to use google, or anywhere else on Stack Overflow before you ask a question. I found this in a minute. If you're adding to a navigation controller (which is what handles views in a table view), then use – pushViewController:animated: instead of presentModalViewController:animated: And after you push or present a view controller, don't forget to release it if you are not using automatic reference counting.

load view controller when application becomes active

I will like my application that always starts as the first time I open it. I have several view controllers and when I exit my application and I open it again I see the view controller where I left of. Maybe I have to call the method applicationWillTerminate method.
I use this code to open a new view:
UIViewController *control = [[SomeViewController alloc] initWithNibName:#"SomeViewController"
bundle:nil];
UINavigationController *navControl = [[UINavigationController alloc]
initWithRootViewController:control];
[self presentModalViewController:navControl animated:NO];
[navControl setNavigationBarHidden:YES];
[control release];
[navControl release];
this code works great when linking it to buttons. But when I place that code in the applicationDidBecomeActive method it does not work.
The easiest way is to set UIApplicationExitsOnSuspend in Info.plist.
That really isn't the expected behaviour, though. Users expect to see the app "where they left off", especially if they've only briefly left the app (e.g. because they got a phone call).
Your code snippet adds a view controller, but is unlikely to work since your app delegate is not a UIViewController. It also doesn't do anything about removing the old view controllers.
EDIT: If all you need to do is display a splash screen (or something), then it's something like this:
In -applicationDidEnterBackground:, add a "splash screen" view (not a view controller) to self.window. (iOS takes a "screenshot" after you return from -applicationDidEnterBackground: and uses this in the app-switch animation; you want this to be what the user sees when switching back to your app)
In -applicationWillEnterForeground:, do whatever animations you want and eventually remove the view from the window (call -removeFromSuperview).
EDIT 2: The same will work in -applicationWillResignActive:/-applicationWillBecomeActive:, except this happens on a sleep/wake event, which might not be what you want...
I'd avoid using view controllers for this, because trying to shoehorn a view controller in the view controller hierarchy is likely to be problematic (for example, you have to figure out which VC to present it from, and you have to do the "right thing" if the user backgrounds your app while the VC is on screen, and...)
The reason it doesn't work in applicationDidBecomeActive is that method is only sent to the Application delegate, which doesn't know about presentModalViewController.
I suggest instead that in your appDelegate, implement applicationWillEnterForeground:, which should restore the state to a newly launched application (equivalent to what the state is at the end of application:didFinishLaunchingWithOptions: ).
OR...(edits)
If you just want a certain viewController to run (which is still loaded, right?)...For example, if you have a tab controller and just want to go to the root of the first view controller, put the following code into applicationWillEnterForeground:
UITabBarController * myTabBar = self.tabBarController;
myTabBar.selectedIndex = 0;
[[myTabBar.viewControllers objectAtIndex:0] popToRootViewControllerAnimated:NO];
Temporary solution:
I made my application crash on applicationWillResignActive method and it works. My application needs to run an animation when I launch it. But this works because next time the application runs it starts like the first time I opened it.

How to load more than one ViewController when using TabBarController?

I have a TabBarController with 7 Custom ViewControllers and what i am trying to do is have the TabBarController on startup load the first ViewController in its array as well as a ViewController in one of the other tabs.
As far as i understand it the viewDidLoad method for the ViewController's is only called for that tab when it is first selected. Is there a way to force the TabBarController to call a ViewController viewDidLoad method even if its not active?
thx
Just reference the ViewController view:
[myViewController view]
If myViewController's view is nil, then it will be loaded.
Note that even if this approach is working, your app will be affected by the view loading/unloading mechanism which is controlled by the internal run loop logic and not by your app, while the view controller "internal logic" should be initialized by the initWithNib: method which is completely under your control. But this is just a suggestion to avoid strange bugs, anyway the solution proposed is working.
This technique seems to work pretty good. I have an app with a UITabBarController at the bottom with 5 buttons in it. When the user clicks the 3rd button, the viewDidLoad for that view took 5 seconds to do stuff so I used this technique to cause its viewDidLoad to get called when the app starts. I don't want the 3rd view to be displayed; just to be initialized so it shows up instantly when clicked.
I had to subclass the UITabBarController to something like FoohBarController, then in it's viewDidLoad I made a background thread do this:
{
// get a pointer to the 3rd item in the tab bar (0 based)
UINavigationController *navcon = [self.viewControllers objectAtIndex:2];
// get a pointer to the viewController I want to init
CalendarViewController *calendar = [navcon.viewControllers objectAtIndex:0];
// Just ask for the view. This will force the view to load and to initialize
UIView *v = calendar.view;
v = nil; // to remove a compiler warning
}

Hoew to implement Navigation on button click

my project first used the Three20 frame work for handling navigation.But due to some problems i ha to remove the three20 part.So im redesigning my app.
what im trying to do is show a detail view when user touches a button.I created a new mainwindow as there was none earlier.It does not have a navigation controller.
i used
DetailViewController *detailViewController=[[DetailViewController alloc] init];
[self.navigationController pushviewController:detailviewController animated:YES]
in the buttos touch up event.
but when i run the app nothings hapens on button touch.No warnings or errors are shown.
Ive done this in other apps but it just doesnt work here.
Since you are not using a UINavigationController, the navigationController property of the your UIViewController will remain nil. "Compile-wise" there's no problem, since the property exists, but the compiler does not know the property will remain nil, hence no warnings/errors are shown.
Sending a message to a nil object is allowed in Objective-C, hence the code doesn't crash and does nothing when being run.
If you want to get your code up & running, you'll need to put the root UIViewController in a UINavigationController. Once the UIViewController is shown in a UINavigationController, the property will be set to the UINavigationController automatically.
Edit: You could use something like this, instead of showing someController, "encapsulate" it in a UINavigationController and show that controller.
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:someController];

Navigation & View Controller questions

I'm experimenting with ViewControllers & NavigationControllers in Interface Builder trying to get a better grasp of what's tied to what and why... I'm struggling with a scenario that has confused me. Hopefully someone can set me straight...
Say I start with your typical iPhone template View-Based Application and I display a view which is handled by view controller (viewController). Then after a certain event I'd like to replace that view with a "typical" Navigation-Based View (rootVC). I'd like to create as much as possible in IB. My questions have to do with how to show rootVC and remove all traces of the previous viewController as user will never need to return and where/how to wire in the navController in IB. Currently when it's time to show the rootVC I do the following in my viewController:
RootVC *rvc = [[RootVC alloc] initWithNibName:#"RootVC" bundle:nil];
[rvc.view setFrame:[[UIScreen mainScreen] applicationFrame]];
ViewTestAppDelegate *appDelegate = (ViewTestAppDelegate *)[[UIApplication sharedApplication] delegate];
self.rootVC = rvc;
[rvc release];
[appDelegate.viewController.view removeFromSuperview];
[appDelegate.window addSubview:rootVC.view];
[appDelegate.viewController release];
rootVC displays except viewController still has a retain count of 1?!?
Also, where should rootVC's navigationController be instantiated? Having started with the View-Based template the MainWindow.xib contains an object for the viewController (which has its own ViewController.xib) an appDelegate and a UIWindow. My RootVC.xib contains a UITableView. Do I need yet another intermediary view controller that will have another ApplicationDelegate object that I wire up to a UIWindow object and a UINavigationController? The View Controller that comes along with IB's Navigation Controller object would then be set to my RootVC class?
Sorry for the verbosity. It's difficult for me to explain. Because some objects in IB are proxies and some are "real" it's sometimes confusing (to me) when trying "new" things out what's required, where & when. Basically I want to know to go about setting up one view leading to another with no way back to first view. 2nd view basically becomes the "main" root spawning off in many directions...
I would recommend using the navigation-based iPhone application template and presenting your one-time view as a modal view on top of the root view.
I was able to figure it out by putting a reference to the viewController in the MainWindow nib and then autoreleasing the viewController after I added the navigationController & rootVC to the UIWindow. Learned another thing or two about IB along the way. Pretty powerful...