Initializing custom UIViewController - iphone

I have TabBar based iPhone application, and in app delegate 2 default view controllers are initialized by apple (if you choose tabbar base app when creating application).
UIViewController *rootViewController = [[tabBarBetFirstViewController alloc] initWithNibName:#"tabBarBetFirstViewController" bundle:nil];
UIViewController *accountViewController = [[tabBarBetSecondViewController alloc] initWithNibName:#"tabBarBetSecondViewController" bundle:nil];
Why this isn't initialized like this:
tabBarBetFirstViewController *rootViewController = [[tabBarBetFirstViewController alloc] initWithNibName:#"tabBarBetFirstViewController" bundle:nil];
tabBarBetSecondViewController *accountViewController = [[tabBarBetSecondViewController alloc] initWithNibName:#"tabBarBetSecondViewController" bundle:nil];
???
Is that the same ? Or it's just those default that are added by apple? If i want to add one more tab will I write:
UIViewController *third = [ThirdViewController alloc].....];
or
ThirdViewController *third = [ThirdViewController alloc]....];
Of course at the end I have:
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.viewControllers = [NSArray arrayWithObjects:rootViewController, accountViewController, third, nil];

ThirdViewController is a subclass of UIViewController, so you can write both. But if you later want to use the variable third to invoke methods that are specific to ThirdViewController, then you should use
ThirdViewController *third = [ThirdViewController alloc]....];
Summing it up: In this simple scenario there is no single right way of "doing it". The important lesson to take from this question (if it wasn't clear already) is to understand why you can assign a ThirdViewController instance to a UIViewController variable (because of the subclassing relationship).

You use the
ThirdViewController *third = [ThirdViewController alloc]....];
approach. Don't know why Apple uses the other approach. I this easy example it doesn't make any difference. But when you have properties you want to set it's better to use the class name.

It depends, if you have a view controller that you want to have a custom interface, you will want it to be a subclass of UIViewController. If ThirdViewController is a subclass of UIViewController then that code that you stated here:
ThirdViewController *third = [ThirdViewController alloc]....];
Would produce the desired result. Apple's approach is just for a generic View Controller without any properties, so ideally you would want all of your tabs to be UIViewController subclasses.

1) If you want to make use of any instance methods or properties in your ThirdViewController, then you must use
ThirdViewController *third = [ThirdViewController alloc]....];
2) If you dont have a need to do so, you can use
UIViewController *third = [ThirdViewController alloc]....]; // it'd make no difference
To be on a safer side, imo, first case is a good practice.

In this case I do not see any difference, I would prefer doing it your way. But in a situation similar to the one below, Apple's way seems better:
UIViewController *vc;
if ( some_case ){
vc = [YourViewController1 alloc]// ...;
[ (YourViewController1 *) vc doSomeThing]; // You might need to use casting for instance messages
//...
}
else {
vc = [YourViewController2 alloc]//...;
}
[self.navigationController pushViewController:vc animated:YES];
[vc release];

Related

How do I pass object from one ViewController to another ViewController

I have a RootViewController that calls an AddQuoteViewController and there is a variable "subject_id" that I set in the RootViewController that does not show up in AddQuoteViewController and I need help understanding why and how to fix it.
Subject *sub = [self.tableDataSource objectAtIndex:indexPath.row];
self.subject_id = sub.subject_id;
[self addQuote_Clicked: sub];
...
- (void) addQuote_Clicked:(id)sender {
if(aqvController == nil)
aqvController = [[AddQuoteViewController alloc] initWithNibName:#"AddQuoteView" bundle:nil];
if(addNavigationController == nil)
addNavigationController = [[UINavigationController alloc] initWithRootViewController:aqvController];
[self.navigationController presentModalViewController:addNavigationController animated:YES];
}
Then in my AddQuoteViewController I try to access this variable like this:
RootViewController *rv = [RootViewController alloc] ;
NSLog(#"rv.Subject_id = %d", rv.subject_id);
But get nothing. There must be a simple way to do this.
First of all, there's an error in your third block of code. This:
RootViewController *rv = [RootViewController alloc] ;
Should be this:
RootViewController *rv = [[RootViewController alloc]init];
But strictly speaking that's not why you aren't seeing your instance variable.
If I understand correctly, the first two blocks of code are in RootViewController, and they instantiate an AddQuoteViewController and present it. Then, from your third block of code, which is in AddQuoteViewController, you want to access a member variable (subject_id) of the RootViewController that brought it up.
The approach of instantiating a RootViewController from within the AddQuoteViewController wouldn't work, because you're creating a different instance of RootViewController. What you're after is the value in the instance you just came from.
Perhaps the easiest way to do it is to create a corresponding property on AddQuoteViewController and set it when it's created:
- (void) addQuote_Clicked:(id)sender {
if(aqvController == nil)
aqvController = [[AddQuoteViewController alloc] initWithNibName:#"AddQuoteView" bundle:nil];
if(addNavigationController == nil)
addNavigationController = [[UINavigationController alloc] initWithRootViewController:aqvController];
aqvController.subject_id = self.subject_id;
[self.navigationController presentModalViewController:addNavigationController animated:YES];
}
You'll need to create the subject_id property on AddQuoteViewController the same way you did on RootViewController.
There are various ways of doing this, but a short answer - You can set a reference to the RootViewController as a property on your AddQuoteViewController
i.e.
in AddQuoteViewController.h
RootViewController *rvc
...
#property (nonatomic,assign) RootViewController *rvc;
and corresponding synthesize and release in your implementation class. (AddQuoteViewController.m)
Then when you create your AddQuoteViewController inside your RootViewController, also set this property:
- (void) addQuote_Clicked:(id)sender {
if(aqvController == nil)
aqvController = [[AddQuoteViewController alloc] initWithNibName:#"AddQuoteView" bundle:nil];
aqvController.rvc = self;
... etc.
Then you can access any property of the root view controller inside your AddQuoteViewController via this property:
NSLog(#"rv.Subject_id = %d", self.rv.subject_id);
As a side note there are a few things you are doing in your question that are a bit unusual, like trying to get a reference to an object by allocating a new one, and also creating a new navigation controller and presenting it as a modal view controller typically you would do one or the other (and wouldn't need to create a new navigation controller). i.e. you would either create a view controller and present it modally, or you would create a view controller and push it onto your current navigation controller stack.
You can't access variables across view controllers like that.
Have a look at creating a singleton class which will be globally accessible.
One exception I think is you can access them in the AppDelegate, but it's not advisable to have global vars.

UINavigationController: push self?

I have a custom class which is a UITableViewController, this is inside of a UINavigationController. Normally, when I click a cell, I push a new class onto the stack and it is fine. This time, I would like to push self onto the stack (passing a different string onLoad so that I load different content), so that I can reuse my code, is this possible? Or do I always have to create a secondary class to push?
Rather than "pushing yourself", you can push a new instance of the same view controller yes. Simply create a new one like so:
MyViewController *viewController = [[MyViewController alloc] initWithNibName:"MyView"];
viewController.customString = #"Something else";
[self.navigationController pushViewController:viewController];
[viewController release];
I haven't tested this, and it's late so there might be bits wrong but you should be ok with that. Let me know if it works!
My solution:
MyViewController *viewController = [[MyViewController alloc] initWithNibName:"MyView"];
viewController.customString = #"Something else";
[self.navigationController pushViewController:viewController];

Change from UIViewController to UITableViewController

This one make me go crazy. I am building an iphone app where the first view is a login view. UIViewController, when the user succesfully logs in i want to display i table view. Somehow i just have big problems doing this.
In my app delegate i load my loginViewController, and then i want from the loginViewController load my listViewController.
What is the logic behind switching to a UITableViewController from a UIViewController?
you'd better to do it in your app delegate and surely NOT add the UITableViewController.view to the UIViewController.view... just add it to the UIWindow and then dismiss the old UIViewController (removeFromSuperView it's view and then release it)
EDIT:
that's how i manage:
i add a method in my appDelegate:
-(void)switchMainView;
and from my UIViewController i just call it with this:
[[[UIApplication sharedApplication] delegate] switchMainView];
in switchMainView i just
remove my UIViewController.view from superview,
release UIViewController,
alloc the UITableViewController and init it, then
add its view to the window app:
-(void)switchMainView{
if (mainView!=nil){ // mainView is the UIViewController
[mainView.view removeFromSuperview];
[mainView release];
mainView = nil;
}
Menu *vc; // Menu is my class, subClass of a UITableViewController
vc = [[Menu alloc] init];
nc = [[UINavigationController alloc] initWithRootViewController:vc];
[window addSubview:nc.view];
[vc release];
}
and then i do the same for going back, eventually
Assuming you already have your custom UITableViewController created:
YourTableViewController *vc = [[UITableViewController alloc] initWithStyle:...];
[self presentModalViewController:vc animated:YES];
[vc release];
you can use either i do'nt think there is a major impact but definitely they might have some advantage/Disadvantage over other..
for better understanding read the below tutorial.
http://cocoawithlove.com/2009/03/recreating-uitableviewcontroller-to.html

How do I pushViewController/etc. from a UIViewController subclass?

I've been attempting to figure this out for a while now, but I'm up to a point where I can't seem to solve my problem from reading other Q&As. I'm trying to get the active UIViewController in a UINavigationController to send popViewController/pushViewController messages to the UINavigationController, but I cannot figure it out. I'm probably doing something rather stupid that is causing it to break. The structure should be like this, but even then I'm not sure if I've done that right.
mainController
primaryNavigationController
firstViewController
secondViewController
both firstViewController and secondViewController are a subclass
mainController.m
firstViewController = [[FirstTestViewController alloc] init];
secondViewController = [[FirstTestViewController alloc] init];
primaryNavigationController = [[UINavigationController alloc]
initWithRootViewController:firstViewController];
[primaryNavigationController.view setFrame:CGRectMake(0,0,320i,409)];
[self.view addSubview:[primaryNavigationController view]];
[primaryNavigationController.navigationBar setFrame:CGRectMake(0,0,20,44)];
primaryNavigationController.navigationBar.tintColor = [UIColor blackColor];
How can I tell primaryNavigationController to push/pop a VC from within the firstTestViewController subclass?
You would allocate the second view controller within your first view controller (because you don't need it before):
secondViewController = [[FirstTestViewController alloc] init];
[self.navigationController pushViewController:secondViewController animated:YES];
[secondViewController release];
The SDK includes many sample projects that involve a navigation controller and show you how to do this.

pass data to object of uiviewcontroller on iPhone

I have an iPhone app with a tableviewcontroller. When you click a certain cell it opens a new uiviewcontroller with this code:
nextViewController = [[avTouchViewController alloc] initWithNibName:#"avTouchViewController" bundle:nil];
The uiviewcontroller above called avTouchViewController has a property that looks like:
IBOutlet SomeObject *controller;
SomeObject is an object with all relevant view properties.
I would like to pass an nsstring parameter from the tableviewcontroller I initialize the avTouchViewController with to someObject.
How can I do this?
I'm a little confused by your question; you say you're creating your avTouchViewControllers when a cell is tapped inside an existing UITableView, but your last part describes the inverse situation.
Basically, if you want to pass information to a view controller, just give it a property that can be set (which may already be the case), e.g.:
nextViewController = [[avTouchViewController alloc] initWithNibName:#"avTouchViewController" bundle:nil];
nextViewController.controller = theInstanceOfSomeObjectIWantToPass;
You also may want to rename your controller property. To a reader, it doesn't make sense that a view controller has a property called controller which is actually a SomeObject*. As well, your class names should be capitalized, i.e. use AvTouchViewController instead of avTouchViewController.
If I were doing this I would add my own initializer to my UIViewController subclass:
- (id)initWithController:(NSString *pController) {
if (self = [super initWithNibName:#"avTouchViewController" bundle:nil]) {
self.controller = pController;
}
return self;
}
And then just call the following (in tableView:didSelectRowAtIndexPath: or whereever):
NSString *controller = #"Sample String";
AVTouchViewController *nextViewController = [[AVTouchViewController alloc] initWithController:controller];
[controller release];
[self.navigationController pushModalViewController:nextViewController animated:YES];
[nextViewController release];
As a point of style, class names conventionally begin with uppercase letters (hence my change to AVTouchViewController).