UIViewController ( init and initWithNibName ) - iphone

Wish to know more about the practical differences between init and initWithNibName. SO answers such as this suggests that it is better to call "initWithNibName" indirectly through "init".
Are there any circumstances that we need to define "init" and
"initWithNibName" differently ?
Is it possible that any Nib file needs to be loaded more than once
during a single program execution ?
Are questions 1 & 2 inter-related ?

It is not better to call initWithNibName: indirectly through init. You just want to call initWithNibName: at some point. You can do that externally or internally. Some people think it's better to do it internally. I actually have a class called "LayoutUtil" that I keep layout-related helper methods to avoid writing tedious piece of layout-related code over and over. Here is my code to load a UIViewController:
+ (id)loadController:(Class)classType {
NSString *className = NSStringFromClass(classType);
UIViewController *controller = [[classType alloc] initWithNibName:className bundle:nil];
return controller;
}
And then I can just do:
MyViewController *c = [LayoutUtil loadController:[MyViewController class]];
If you want, you could add a method called ughhhh to a class and call it in there, it doesn't matter at all. The point is that it is not a better practice to call initWithNibName in the init method though, you just want to make sure you call it at some point when initiating a UIViewController.
- (id)ughhhh
{
self = [super initWithNibName:#"Myview" bundle:nil];
if (self != nil)
{
}
return self;
}
A nib file can definitely need to be loaded more than once. Everytime you call initWithNibName on a UIViewController the xib has to be loaded. A lot of people load UIViews that are not owned by a UIViewController like this:
[[NSBundle mainBundle] loadNibNamed:#"nameOfXIBFile" owner:self options:nil];
Everytime you call this function you will be loading the nib.
There are certain cases where a nib can be cached. An example would be a UITableView -- but the table view implements it's own cache. The operating system isn't doing any caching automatically.
init and initWithNibName: are related in that initWithNibName: automatically calls init on an object.

It's not ‘better to call "initWithNibName" indirectly through "init"’. You should use whichever one suits your needs better. Saying [[UIViewController alloc] init] is exactly like saying [[UIViewController alloc] initWithNibName:nil bundle:nil], so if those are the arguments you want to pass, you might as well use [[UIViewController alloc] init].
In answer to your questions:
You can define init and initWithNibName:bundle: differently if you want to. You can define just one of them. For example, UIImagePickerController only defines init, and if you try sending it initWithNibName:bundle:, it won't work properly. Or you can define some entirely different init... method. For example, UINavigationController only defines initWithRootViewController:. Whatever init methods you do define must eventually call one of its superclass's init methods.
Yes, a nib can be loaded multiple times. If you create multiple instances of the same view controller subclass, it's likely that you will load the same nib multiple times. In fact, it's possible for a single instance of a view controller to load its nib multiple times. How? If a view controller's view is not currently on screen, and the system runs low on memory, the system will ask the view controller to release its view. If the view controller later needs to put its view back on the screen, it will load the nib again.
Questions 1 and 2 are not related.

Answer of first question
Ideally every initWithNibName calls init internally so you do not need to define both in normal case Scenario , but you can if View is also loaded from loadView in one case and fron Nib as well in another case from different Views.
Answer of Second question
Everytime View is pushed in Stack Nib is loaded
Answer of Third question
No

Related

iPhone Programming - Passing NSMutableArray between view controllers

I have a RootViewController and a DetailViewController where I am trying to pass a NSMutableArray from the rootView to the detailView. I have created an object of the RootViewController inside the DetailViewController and accessing it like the following
RootViewController *root = [[RootViewController alloc] initWithNibName:#"RootViewController" bundle:nil];
detailViewArray = [root.rootViewArray copy];
Note: Both the arrays are declared in the .h files; synthesized and then allocated and initialized array = [[NSMutableArray alloc] init];
Problem: I am not too sure why it still doesn't work. I have tried a lot of the solutions on the internet but it didn't quite work for me. The property for the root array is nonatomic, retain Is it something wrong with that? Do I need to change it to something or the method I am following is just not right.. Please if someone could help!
If you alloc/init the RootViewController inside the DetailViewController, you are creating another instance of the RootViewController. You are not getting the same instance (with data) of the rootViewController.
That said, even passing a reference of a viewController to another viewController to then poke at it's data is sort of bad. It creates tight coupling between the views.
Instead, if you need to get data, consider using a delegate to communicate between the views.
What exactly does delegate do in xcode ios project?
Tutorial:
http://www.theappcodeblog.com/2011/04/15/passing-data-between-views-tutorial-using-a-protocol-delegate-in-your-iphone-app/
Another option is to create a shared model (read up on model view controller patterns). It's typical in that pattern to create a model and share data by getting a singleton instance of your model:
MyModel *model = [MyModel sharedInstance];
Then, each view can set and read data from that same (singleton) instance of the model.
Which to choose? The model is better if many views share the same data. A delegate is appropriate for a couple views to communicate with each via callbacks.
Even there is an another way to pass array.
MyViewController mvc = [[MYViewController alloc] initWithNibName:#"MYViewController" bundle:[NSBundle mainBundler]];
[mvc getDataArray:<pass your array>];
[self.navigationController pushViewController:mvc animated:YES];
Here it will set a dataArray property first then it will push the view controller to MyViewController class.
This will be useful when you are very much concerning about memory. Because if you are using static (sharedInstance) then its scope persistance within the entire application scope.

How to load a UIViewController programmatically?

I'm trying to load a UIViewController from code. I basically created a UIViewController generating a .xib file. I load it with the following code:
testViewController = [[TestViewController alloc] initWithNibName:#"TestViewController" bundle:nil];
this works but apparently (from what I have read) only the view is kept and the controller is deallocated (something which I have tested putting a breakpoint in the UIViewController's dealloc method). I need to keep the UIViewController or otherwise, create a UIViewController with a UIView loaded from a nib file. How can this be achieved?
Not sure what you mean. What you're doing there is correct. Since you've allocated it, you also -need to send a release statement to the view controller at some point, till then it shouldn't be dealloc'ed.
A view might get released and set to nil under a special case where the application receives a memory warning, but I'm not sure if this was what you've read.

Calling function in the first view from the other viewcontroller

I hope that you will succeed in at least a little to clarify me how and what to do, I'm sure I'm wrong, so I really need an expert opinion.
I have two viewcontroller together with nibs what I want is to call function that is in first class from another ViewController, the problem is that another viewcontroller manages to call this function but the only thing I can see that is done is NSlog which outputs in the console (which I put in that position to know that the function is called)
example:
FirstViewController.m
-(void)drawingFunction:(NSString*)inputText{
NSLog("Feature was launched");
/* showing stuff in FirstView.xib */
}
SecondViewController.m
-(void)turnOnFunction:(id)sender{
FirstViewController *stuff= [[PrviViewController alloc] init];
[stuff drawingFunction:#"ShowAll"];
}
Basically when I call that function in the PrviViewController.m meaning without reference to the outside e.g.
[self drawingFunction:#"ShowAll"];
everything is well displayed, but when I try to call this function from SecondViewController.m all I see is just a "feature was launched" on the console
I really don't know what to do ...
if i get you right, the FirstViewController is on top of the view controller hierarchy (either within a navigation controller or presented modal)? So, if that is the case just creating an instance of SecondViewController and calling a function which should change the user interface on the view which corresponds to SecondViewController will do nothing at all.
You have to present the SecondViewController and its view with presentModalViewController:animated: for instance. After that the view of SecondViewController will be shown and you can do any changes on that view. Also calling the method drawingFunction: will act as you expect it.
Cheers,
Andreas
You're confusing instances with classes. You don't send messages (call functions) to classes, you send them to instances.
In this case, you have two entirely separate instances of FirstViewController. The first is created by loading the nib and the second is created in -[SecondController turnOnFunction:]. The drawingFunction: message is being sent to the second instance when you want it to be sent to the first.
You need to link the nib instance of FirstViewController to the SecondController instance. You can do that with an outlet in interface builder or you can assign it when you create the display the SecondController.

Who should call viewDidLoad on programmatically loaded views?

When I need to load a view programmatically I do the following:
MyController* myController = [[MyController alloc] init];
[[NSBundle mainBundle] loadNibNamed:#"myNib" owner:myController options:nil];
// use my controller here, eg. push it in the nav controller
This works fine, but my controller's viewDidLoad is never called. So I resorted to manually calling it after the loadNibNamed call, but it doesn't seem correct. I was expecting the framework to call the viewDidLoad on my behalf. Is this the right way or I'm missing something?
I am new to Stack Overflow, but I discovered this question and discovered a couple other methods to load a nib file that ensure that the viewDidLoad method gets called automatically. Ed Marty's answer is correct, but it requires you to go outside the ViewController's code to load the nib file, these two examples I offer here allow you to keep your code inside the ViewController's implementation. Not necessarily better ways, just different. If you know of any drawbacks to doing it this way, please let me know your thoughts.
First, inside the initWithNibName:bundle: method of your UIViewController subclass, you can replace the call:
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
with something like this: self = [super initWithNibName:#"NameOfAlternateNibFile" bundle:nibBundleOrNil];
Or, you can accomplish what appears to do exactly the same thing by the following:
[[NSBundle mainBundle] loadNibNamed:#"NameOfAlternateNibFile" owner:self options:nil];
[self setView:self.view]; //this line ensures that the viewDidLoad method is called
The key is in understanding what is written in the comments above the function definition for $initWithNibName:bundle: in the UIViewController.h file (included at the bottom of my answer here, see italics).
The nice thing about doing this using either of these methods is that viewDidLoad gets called in either scenario.
Here are the directives listed in UIViewController.h:
The designated initializer. If you
subclass UIViewController, you must
call the super implementation of this
method, even if you aren't using a
NIB. (As a convenience, the default
init method will do this for you, and
specify nil for both of this methods
arguments.) In the specified NIB, the
File's Owner proxy should have its
class set to your view controller
subclass, with the view outlet
connected to the main view. If you
invoke this method with a nil nib
name, then this class' -loadView
method will attempt to load a NIB
whose name is the same as your view
controller's class. If no such NIB in
fact exists then you must either
call -setView: before -view is
invoked, or override the -loadView method to set up your views programatically.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil;
You should load view controllers with
MyController* myController = [[MyController alloc] initWithNibName:#"myNib" bundle:nil];
Make sure your MyController extends from UIViewController, and the View property is set properly in Interface Builder.
The views are loaded lazyly by UIViewController. If you use the accessor myController.view in your code, the view should be loaded and viewDidLoad be called.
I noticed the same thing. I think ViewDidLoad must be called by the view CONTROLLER. Since you don't have a view controllr in your nib, you have to call the viewdidload manually. I"m having to do the same thing.

What's the best way to create grouped table view controllers on the iphone?

UITableViewController has an initWithStyle method where you pass it the style you want the table view to use, plain or grouped. These means that to create and use my table view controller, somewhere else in the code, possibly in multiple places, I do this:
MyViewController *myViewController = [[MyViewController alloc] initWithStyle: UITableViewStyleGrouped];
[self.navigationController pushViewController: myViewController animated: YES];
[myViewController release];
These seems backwards to me. The classes that use my UITableViewController should not know or care if it used grouped or plain style, that is a concern that the MyViewController should take care of. What's the idiomatic way of dealing with this? Override the init method in MyViewController to call initWithStyle?
My answer as a newbie is 'just do it' and worry about the philosophical design issues later. All the examples I've seen, including popular books have that 'pattern' so it can't be too wrong.
Just be careful when overriding one init method on a superclass that calls another. I'm not sure about it here, but IF initWithStyle allready calls init in the superclass when you override init in the subclass and call the super you might have a loop on your hands.
I would just create a table view controller through xcode, assign the table the grouped style in Interface Builder, then override the init method if necessary:
- (id)init {
self = [super initWithNibName:#"MyViewControllerNibFileName" bundle:nil];
if (self) {
// Do other customisation
}
return self;
}
I needed to explicitly set the nib name for some reason, but sometimes the view controller just finds it automagically (I'm still a newbie, so not sure why this happens)