viewDidLoad, awakeFromNib, initWithCoder:...Where to init members? - cocoa-touch

Just a simple question...I'm using Interface Builder for creating my view, and I wonder where I should initialize my UIViewController's member variables (which are not IBOutlets)...viewDidLoad? awakeFromNib? initWithCoder:?
Thanks for your answers!

Use awakeFromNib if you need to do anything extra with your IB outlets before the view is actually loaded (so at the time post when the NIB is loaded), otherwise you can normally initialise member variables in viewDidLoad:
This method is most commonly used to perform additional initialization steps on views that are loaded from nib files.
In general, if you follow how Apple's samples initialise member variables you'll be fine; you only need to consider changing the location of member initialisation if it's not appropriate to do it when the view is loaded.

Related

Objective C: init and awakeFromNib

I've recently studied some Cocoa based open source projects. I saw that a lot of programs have all initializing code in awakeFromNib and rarely use the designated initializer. I am used to do it that way:
in the overridden designated initializer: do all non-Nib stuff
in awakeFromNib: do all Nib-related stuff
Is this approach wrong?
Yes, it's correct, all nib-related stuff should be done in awakeFromNib method. At the moment when it's called you already have initialized and loaded view from nib, so you already may configure and use it.
As concerned to initializers, please, check this question: iOS: UIView subclass init or initWithFrame:?
The designated initializer is the one that all the other initializers must call. UIView and subclasses are a little unusual in that they've actually got two such initializers: -initWithFrame: and -initWithCoder:, depending on how the view is created. You should override -initWithFrame: if you're instantiating the view in code, and -initWithCoder: if you're loading it from a nib. Or, you could put your code in third method and override both those initializers such that they call your third method. In fact, that's often the recommended strategy.

Cocoa - explanation of view handling

I'm learning Cocoa at the moment, and have followed 'generic' tutorials on getting a basic form with a button and label.
With the .xib, I've added an 'Object' (instance of NSObject subclass), and have also created a ViewController class, which I connect to my view by setting Custom Class to ViewController. I then code up my ViewController.h and .m files adding a pressedButton method, and a label (myLabel). This all works OK (ie. the label updates when the button is pressed).
My question is: what am I actually doing with this process in C++ terms (a language I'm more familiar with)? As I understood it, my 'Object' (set to class ViewController) represents an instance of the .xib file (may be wrong here), and with this set to the ViewController class, I'm able to make outlets and actions in ViewController.h/.m, but I'm still not sure what's really going on behind the scenes.
Lastly, the XCode template provides an AppDelegate class 'free'. Given I'm managing my controls via my ViewController class, what would this file/object be used for - is it for application specific things that do not relate to the view itself, or is it also used to manage the controls on the form too (like you see in some tutorials I think)?
I hope, I understood the first part of your question well. The .xib (xml verison of nib file) does not represent a class or object. It is rather used to create a view object with all its subviews (each an object) and link it properly to your view controller.
It is most common and automatically generated that way, that the underlying view within your nib file corersponds to self.view (from your view controller's point of view).
You could access each view created by the nib file by navigating though the subview hierarchy of self.view. Alternatively you could define tags within nib/IB (Interface builder) to access individual view objects more easily.
"Just for your convenience" the IBOutlets were introduced. You can define an IBOutlet in the interface of your view controller and link it using IB to the dedicated object that is created using the xib file.
I am saying "Just for your covenience" because it is not really neccessary, considering other ways of addressing your view objects. But it is more than convenient and therfore strongly recommended and stimply the standard way of doing it.
You could even add further views programmatically to a form/view that was created using IB. You can change properties of those views programmatically.
The view and the subvies are created in that very moment when your view controller is initialized with a nib file using the initWithNibName:bundle: method. Then the nib file (xib these days) is opened, its content is parsed and interpreted and all views are created and added to their superviews respectively and their properties and action: selectors etc. are set accordingly. Again, the whole process is designed for your convenience. You could even create all the views etc. programmatically - witout any nib file at all. The loadView method of your custom view controller would be the appropriate place of dong so.
Second question:
AppDelegate is designed for application wide actions etc. If you can manage everything fine within your view controllers then you would not need to change anything on the AppDelegate.
It is often used as a container for variables or functions of global character (which are then properties and members of the app delegate object). Sometimes you may see it neccessary to override some of the AppDelegates methods like application:didFinishLanuncing or applicationDidBecomeActive.
Strictly spoken, UIApplicationDelegate is no class that you subclass. It is a protocol that you implement. (Very much like interfaces within Java - sort of overcoming the lack of multiple inheritance that you are familiar with from C++).

How is view initialized when loaded via a storyboard?

When view is loaded manually, developer remains in control when it comes to initializations, we choose what initializer to call, what variables to set etc.
When view is loaded from the storyboard segue ... what happens to that initializer? Where should variables be set i'd like to be available once view had been loaded?
Please help me understand the sequence here. How is instance of the class created here, who creates it and how can we intervene and help set it up to our liking?
When a view is loaded from a nib or storyboard, it's -initWithCoder: method is called. Like -initWithFrame:, -initWithCoder: is a designated initializer for UIView. If you're going to do any custom initialization for a UIView subclass, you should make sure that it happens for both these methods. One common technique is to add a common initialization method that you call from both -initWithFrame: and -initWithCoder:. See my answer to Custom view and implementing init method? for a more detailed description.
Note that the documentation for -initWithFrame: explains:
If you use Interface Builder to design your interface, this method is
not called when your view objects are subsequently loaded from the nib
file. Objects in a nib file are reconstituted and then initialized
using their initWithCoder: method, which modifies the attributes of
the view to match the attributes stored in the nib file.

How can I initialize a custom GUI class?

I'm developing an iPad app.
Now I ran into the following problem. I created a "Custom Class" for a UIScrollView. So in my nib file I have added a default UIScrollView and set the Custom Class to MultiSelectView (which is the custom class I created). Screenshot here.
So in my MultiSelectView I have added some methods that I want to use. This works!
The issue is that I'm wondering how I can initialize certain objects that I need in these methods. It seems like - (id)initWithFrame:(CGRect)frame {} is not called, and neither is - (void) viewDidLoad {}.
Thus, is there a way to initialize a custom (GUI) class?
When you unarchive a view from a .xib file, it is not sent -initWithFrame:, as you've noticed. Instead, it's sent -initWithCoder:.
So, if you've got any UIView subclass in a .xib file that needs custom initialization, you'll need to override -initWithCoder: as well as (or instead of) -initWithFrame:.
Looks like you need initWithCoder, it is called when object is loaded from NIB
Or, better, awakeFromNib. The difference is that awakeFromNib is called when all outlets are connected.

Reasons for an IBOutlet to be nil

What are the reasons why an IBOutlet (connected) could be nil?
I have one in may application which is always nil, even if I recreate everything from scratch (declaration and control).
It could be that your nib is messed up, but I find a common reason is having two instances where you think you only have one, and the one you're using in your code is not the one you connected.
If you've also defined a loadView method that creates the view, it is possible based on how you initialize it. If you initialize it using alloc-init and the nib name is not the same as class name, then you can have a case where the outlet is nil. But Chuck's answer seems more reasonable to assume.
One reason I just got stung by: If the nib file is not included in the target resource files for some reason (like you had the targets unchecked when you added it to the project), Xcode doesn't throw an error but all the outlets from that nib are going to be null...
One possibility:
Suppose the IBOutlet container is a singleton object with a function like:
+ (singletonObject*) sharedInstance {
if(!gGlobalSingletonPointer) {
gGlobalSingletonPointer = [[singletonObject alloc] init];
}
return gGlobalSingletonPointer;
}
You create the singleton object "on demand" if it doesn't already exist.
You save a global pointer to it, as you create it, in that function.
If you also instantiate such an object in InterfaceBuilder, and connect its outlets, this object will be created without sharedInstance being called. If you subsequently call sharedInstance, a new object is created (sans IBOutlet connections).
The solution is to update the global pointer in singletonObject's init or awakeFromNib function.
Are you using a UINavigationController?
If so, open your MainWindow.xib in IB and make sure that your root controller's nib name is set correctly in the Attributes Inspector.
Why would this not be set correctly? One reason is the 'rename' refactoring doesn't update this, and then the internals won't find the nib with which to wire your UI. Or you renamed the nib yourself, and didn't update this field.
Are you doing something unusual with File's Owner? If you're not in one of the situations where the nib is loaded automatically (main nib loaded by application or nib loaded by view controller, document, or window controller), then you have to load the nib programmatically.