Is a UINavigationController required to do pushViewController? - iphone

I think the answer to my question is "Yes", but i just would like a confirmation from everyone.
I have a UITableView which should "slide" to another UIViewController when i select a row.
I've done this before with other apps (and they've included the UINavigationController, though in a UITabView).
So im wondering:
Is a UINavigationController required in order to use the self.navigationController pushViewController?
If so (which again i suspect to be true), where must I define the UINavigationController?
In the AppDelegate? In the main/primary UIViewController?

Yes, you will need to have a UINavigationController in order to do pushViewController. However, there is no one place where you must define it. If every view controller in your whole application is part of one UINavigationController, then it makes sense to put it in the App Delegate. You can, however, allocate and use a view controller at any time. (Notice that it is a UIViewController subclass.) You can also have multiple UINavigationControllers in your app (which is common, for example, if you have a Tab bar). So you can create UINavigationControllers at any time.
If you are looking for alternate ways of present other view controllers, you do have some different options. For example,
- (void)presentModalViewController:(UIViewController *)modalViewController animated:(BOOL)animated
will allow you to transition from one view controller to another view controller, and can be used without a UINavigationController. But I think the Navigation Controller is very often the best way to move from one view controller to another.

The short answer is: Yes.
UINavigationController was designed with the purpose of managing and animating a stack of view controllers on and off the screen (in conjunction with a few other interface elements such as a navigation bar, or a toolbar). Traditionally, UINavigationControllers are strongly held and initialized by the App Delegate, as they are considered a top-priority root object. An example class showing the proper usage of a UINavigationController might look like this:
#import <UIKit/UIKit.h>
#interface CFIExampleAppDelegate : NSResponder <UIApplicationDelegate>
#property (nonatomic, strong) UIWindow *window;
#property (nonatomic, strong) UINavigationController *navigationController;
#end
#implementation CFIExampleAppDelegate
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc]initWithFrame:UIScreen.mainScreen.bounds];
self.navigationController = [[UINavigationController alloc]initWithRootViewController:/*Some Controller Instance*/];
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
return YES;
}
#end
The long answer, as always, is No.
UINavigationController can be trivially reimplemented (and it has, many times) to great effect. It's as simple as subclassing UIViewController and bolting on some kind of stack (like an NSMutableArray).

Related

Iphone dev: Basic static page navigation?

This is an incredibly basic question, but I'm not sure what's the best method of doing this professionally.
Suppose I want to make an app where the user just taps through multiple pages (a page might just be a screen with a background image and a few buttons). Now and then they might tap back to access a previous page.
I've come from cocos2d, so what's actually the best way to do this with Cocoa Touch? Do I have a seperate view for each page and just keep adding/removing them to the main view controller? Would I load everything at the start or drop a page from memory when the user clicks away?
Please give me a general approach as to how you would do this. Thanks!
The best way to do that is to use a UINavigationController. You can do something like this :
AppDelegate.h
UINavigationController *navigationController;
#property (nonatomic, retain) UINavigationController *navigationController;
AppDelegate.m
#synthesize navigationController;
// In ApplicationDidFinishLaunching
UIViewController *yourMainViewController = [[UIViewController alloc] init];
self.navigationController = [[UINavigationController alloc] initWithRootViewController:yourMainViewController];
self.window.rootViewController = self.navigationController;
in YourMainViewController.m
// When you click on a button
[[self.navigationController pushViewController:yourNewViewController animated:YES];

Why a separate instance of ViewController Class is affecting previous instance?

UPDATE - Cause found!... please read below & suggest the solution:
While creating a video to show this issue, I have found why does that happen...
Any Control/Element that is defined between #imports & #implementation DetailViewController in .m file is lost by the original detVC when a new instance is created of VC.
Example:
I am including a video that re-creates this issue. As shown, all controls work except a UISegmentedControl & a UILabel.. both of which are defined in DetailViewController.m as:
#import "DetailViewController.h"
UISegmentedControl *sortcontrol;
UILabel *incrementLabel;
#implementation DetailViewController
Video Link - http://www.youtube.com/watch?v=2ABdK0LkGiA
I think we are pretty close.. Waiting for the answer!!
P.S.: I think this info is enough can lead us to the solution, but if needed I can share the code too.
EARLIER:
There's a detailViewController (detVC) in our app which is normally 'pushed' from a UITableViewController.
Now we are also implementing Push Notifications which would 'modally' present the same detVC whenever the notification arrives, which in turn can happen over any view controller.
It works all fine until the detVC through notification is presented while another instance of detVC was already the active view controller (pushed earlier through TableView).
The problem happens when the presented detVC is dismissed - the pushed detVC 'looses' control of about everything - It is not pointing to the same data as earlier & even the UISegmentedControl on Navigation Toolbar points to -1 index on pressing any index!
Why does such thing happen when we are creating a separate instance of detVC? Why does it affect the earlier detVC? How can it be resolved / What about Objective C needs to be learned here? (FYI, we are using ARC in the project)
Thanks
EDIT - Some code:
When Pushed:
ProductDetailViewController *detailViewController = [[ProductDetailViewController alloc] init];
detailViewController.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:detailViewController animated:YES];
detailViewController.serverOffset=serverOffset;
detailViewController.prdctIndex = indexPath.row;
When presented through Notification:
- (void)displaySingleProduct:(NSDictionary*)userInfo{
ProductDetailViewController *onePrdVC = [[ProductDetailViewController alloc] init];
UINavigationController *addNav = [[UINavigationController alloc] initWithRootViewController:onePrdVC];
onePrdVC.justOne=YES;
onePrdVC.onePrdIDtoLoad=[[[userInfo valueForKey:#"prd"] valueForKey:#"id"] intValue];
[self.navigationController presentModalViewController:addNav animated:YES];
}
There is a Product class in detVC which gets the data from the values stored at the prdctIndex row in the SQLite table.
After dismissing the presented detVC through notification, the Product class of the pushed detVC starts pointing to the row which was used for Product class in presented detVC. And there is no such code in viewDidAppear which would alter Product class.
So, this, UISegmentedControl issue mentioned above & some other problems are causing the pain! Basically, the whole detVC acts weird - which re-works if we just pop & re-push it!
EDIT 2
I have more info on it which could lead to the cause.
If I run viewDidLoad on viewDidAppear like this:
if (!justOne) {
aProduct = [allProducts getProduct:(prdctIndex+1) one:NO];
[self viewDidLoad];
[self populateDetails];
}
the pushed detVC regains the control & every element/control starts re-working as expected (but not in the ideal way). So, it is quite clear that the separate instance of presented detVC does messes up the earlier pushed detVC & a re-setting up of all the controls / pointers is required through viewDidLoad.
Can any helpful conclusion can be derived from this info?
When in your code you write:
#import "DetailViewController.h"
UISegmentedControl *sortcontrol;
UILabel *incrementLabel;
#implementation DetailViewController
You are not defining these variables as instance variables (ivars), so they are not individual for each instance. There are three ways to define ivars.
1) The traditional way, in your .h file.
#interface DetailViewController{
UISegmentedControl *sortcontrol;
UILabel *incrementLabel;
}
2) Some additions to objective-C have added support for the next two ways. Like declaring them in your .m file.
#implementation DetailViewController{
UISegmentedControl *sortcontrol;
UILabel *incrementLabel;
}
3) If these ivars use properties to expose them, then you can simply leave out the explicit definition of them. So in .h:
#interface DetailViewController
#property (strong, nonatomic) IBOutlet UISegmentedControl *sortcontrol;
#property (strong, nonatomic) IBOutlet UILabel *incrementLabel;
and then in the .m file:
#implementation DetailViewController
#synthesize sortcontrol;
#synthesize incrementLabel;
Are you loading the view controller from a NIB? If so, it may be that the same instance of the view controller is used each time. You can log the address of your view controller in viewDidLoad and see if this is the case.
It actually depends on how your apps architecture has been created. If you are using TabBarController based application, here is the proper way to do so.
If two instances of the same class are interfering with each other, that strongly suggests that you are not storing all your state in instance variables. I would suspect that something in ProductDetailViewController stores its state in global or class data, and that your problem lives in there.
It's possible that you have done something silly like making ProductDetailViewController a forced singleton (overriding +alloc, which you should almost never do), but generally people remember when they've done that.
Never call viewDidLoad directly. That's only for the system to call. (I know you were just testing, but don't leave that in.)
But are you calling [super viewDidAppear:animated] in your viewDidAppear:? You have to call your superclass in that method.

iPhone View Controller Register

Hi i'm pretty new to iPhone development, looking to put together a fairly substantial app and just wondering should View Controllers which are used later in the lifecycle of the app be registered in the AppDelegate at the start of just introduced as needed?
For example I start with a login page which requires a UINavigationController so I register with AppDelegate and i'm away, however following an intermediary page I'm
using a TabController so do I just introduce it on the 3rd page or register in AppDelegate?
More of an architectural best practice issue really :)
When the app launches, the main xib is loaded.
We basically provide the very first vie/view controller when the app launches in the app delegate in the function
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
This very first view controller could be UIViewController, UITabBarController, UINavigationController, etc. In short, any view controller.
From here, your application can proceed by showing new/other view controllers one after another in various ways like presenting a view controller modally, pushing a view controller (in case of UINavigationController), etc.
Well to answer your question short and simple. iPhone apps should use the least amount of memory as possible. So introducing a View Controller when needed is much less memory consuming then keeping everything open and running from start to end.
Hope that answers your question.
Generally, you should only instanciate classes that you need to save memory. If you create you views in code, a good way to do so is to use the getter method of a #property to create the class. For example, if you have a header file with:
#interface MyClass
#property (nonatomic, retain) UIView *myView;
#end
And an implementation file:
#implementation MyClass
#synthesize myView;
- (UIView *)myView {
if (myView == nil) {
myView = [[MyView alloc] init];
// do more initializations
}
return myView;
}
Then you can just access the view at any time, if it hasn't been created it will be, e.g.
[superView addSubView:self.myView];

Navigation Controller and Bar - Memory Managment

first of all... i'm italian, so i'm sorry for my bad english!
so... this is my app:
i'm using a navigation controller: in the first view there are 10 buttons and every button calls a functions like this:
[self pushViewController:nameview animated:YES];
to a different uiviewcontroller! So i have 11 uiviewcontroller!
Every controller is decleared like here
#interface ...
IBoutlet UIViewController *viewcontroller;
...
#property (nonatomic, retain) IBOutlet UIViewController *viewcontroller;
Finally i have to say that i'm working with IB!
My problem is that my app doesn't release memory!
when i'm in a view and i tap on the "backbuttonitem" (created by IB, not by me) the last view doesn't became released (again, sorry for my bad english)... and if an user see all 10 view, the app receive a warning massage (low-memory)!
How can i release the last view saw before the popviewcontroller action?
thanks :D
When you push a view controller onto a navigation controller you need to release it as the navigation controller now owns it. For example you would do the following:
UIViewController *controller = [[UIViewController] initWithNibName:#"Nib" bundle:nil];
[self pushViewController:controller animated:YES];
[controller release];
Then when you use popViewControllerAnimated: the navigation controller will take care of making sure the view controller is released from memory.

How do I get a reference to a rootViewController to a sub-view?

An answer posted for one of my previous questions brings up another question; I am calling a new view controller, "RuleBuilder," from my rootViewController. The rootViewController holds a reference to a contacts array. How do I get a reference to that array into the RuleBuilder? I tried adding
UITableViewController *rootViewController;
...
#property (nonatomic, retain) UITableViewController *rootViewController;
to RuleBuilder.h, and then
#synthesize rootViewController;
in RuleBuilder.m. When I instantiate and push the RuleBuilder from within rootViewController, I do this:
ruleBuilder.rootViewController = self;
But when I try this
[rootViewController.contacts addObject:newContact];
from within RuleBuilder, I get a compiler error to the effect of "request for 'contacts' in something not a struct" (or very similar; I haven't implemented this exact snippet of code, but I tried an identical approach not an hour ago for a couple of different references that I never was able to get working).
Thanks, again, for your help.
You've declared the rootViewController property as a UITableViewController (which does not have a "contacts" property).
Most likely, your root view controller is a subclass of UITableViewController. If you called that subclass RootViewController, then the rootViewController property in RuleBuilder should be declared as
RootViewController *rootViewController