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.
Related
I am developing a document-based app. I am using a dedicated window controller for the document window, and calling the -[NSDocument makeWindowControllers] method.
My window controller is initialized like this:
- (instancetype) init
{
if (self = [super initWithWindowNibName:#"Document" owner:self]) {
}
return self;
}
Here, #"Document" is the .xib file containing the main document window that was created with the project.
The rationale here is that, this window controller is always initialized with this type of window, so the parameter is both hard-coded and hidden away inside the implementation of -init (while also conveniently setting the window owner to self).
So, the side that instantiates the window controller (in my case, the document class) doesn't need to worry about which nib to use and can just call -init.
The problem is, I am breaking the designated initializer chain and Xcode complains with these warnings:
Semantic Issue Method override for the designated initializer of the
superclass '-initWithCoder:' not found.
Semantic Issue Designated initializer missing a 'super' call to a
designated initializer of the super class.
Semantic Issue Method override for the designated initializer of the
superclass '-initWithWindow:' not found.
Semantic Issue Designated initializer invoked a non-designated
initializer.
I would switch the call to -initWithWindowNibName:owner: with one to -initWithWindow: (and after that, set the owner manually, I guess?); but I don't know how to create an NSWindow directly from a nib (or if this is the right thing to do).
EDIT: I just discovered that the warnings are being triggered only because I labeled -init as NS_DESIGNATED_INITIALIZER in my interface. I can remove that label and the warnings go away, but -init is my de-facto designated initializer so I would rather keep it.
Can we rely on the fact that in Objective-C, the rule is that a class's designated initializer is always called for sure? Or can we say, it should be almost always true, except a couple of exceptions? For example, for UIView, the docs says:
initWithFrame:
If you create a view object programmatically, this method is the
designated initializer for the UIView class. Subclasses can override
this method to perform any custom initialization but must call super
at the beginning of their implementation.
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
Or can we say that, if it is programmatically, the rule should always apply for well designed classes, but Interface Builder is a bit different because it sort of "revive" or build the object from a non-programmatic way. If so, are they other exceptions in general when we do iOS programming?
The fact is that class designed with Interface Builder are unarchived and not initialized .
Being archived involves that the class is not initialized but unarchived, so the initWithCoder: method takes responsibility for setting up the control when it's loaded using the archived attributes configured by Interface Builder.
You should put your initialization operations in the awakeFromNib: method that gets called in each case after the object is loaded, thus you will be sure that your initialization statements will be called .
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.
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.
i want to create a subclass of NSWindow. this subclass needs to initialize some member variables before they are used. what is the best way to capture initialization in objective c? what i find is that init rarely gets called in a way that allows me to do this. NSWindow has a couple of initialization vectors that i would need to override. do i need to override each of them?
Each class should have one so-called designated initializer. This is the init method that all other init methods call. That's the one to override. The documentation usually tells you which one the designated initializer is. In the case of NSWindow, it is:
initWithContentRect:styleMask:backing:defer:
This method is the designated initializer for the NSWindow class.
In addition to the designated initializer, you should also override -initWithCoder: if the class you subclass implements the NSCoding protocol. -initWithCoder: is the initializer that is used when an instance is instantiated from an archive (such as a NIB file).
See The Designated Initializer in Apple's "The Objective-C Programming Language".
Have you tried overriding the designated initialiser
- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation screen:(NSScreen *)screen
Documentation