How to pass references to init method of object in a nib? - iphone

I have a UIView subclass which currently assembles itself completely programatically. It has a custom initWithFrame:bundle: initializer which is necessary because it uses the bundle passed in to load image resources.
I want to make this view a subview in a larger nib file, but then initWithCoder will be called when the nib loading code gets to it instead of my custom initializer. Is there any way I can place this view in a nib and still have my custom initialization occur?

Yes, you can override initWithCoder: too if your bundle is known ahead of time (read: You don't need it passed in as a parameter). Otherwise, nope.

If you can wait until awakeFromNib, you can do your initialization in there. You still have the problem of not being able to pass the bundle into the method, though. Since awakeFromNib is called after initialization and setup of all outlets and actions, it might be too late for you. Maybe it's better to redesign around the nib-loading system anyway?

Related

How does UIImage get constructed when created from an XIB?

I'm trying to do some fanciness with XIBs and that includes wanting to somehow get and store the paths of images loaded from the xib. To do this, I made some categories and did some method swizzling to override all the UIImage constructors to save their path before calling their parent constructor.
But due to Apple's black box BS with all their XIB stuff, absolutely none of the exposed constructors for UIImage seem to get called when I create a UIImageView through [UIViewController initWithNib...].
Does anybody know what function call happens or how they do this? I can't find any information whatsoever that exposes what initWithNib actually does behind the scenes.
Thanks!
EDIT:
If you're in a similar situation, you may try using the accessibilityLabel / accessibilityHint which is automatically populated with the image path. The only issue is that accessibility needs to be enabled or these values are nil.
I know the objects constructed from a Nib are being unarchived according to the NSCoding protocol; you need to override initWithCoder: in this case.
You could use swizzling to replace UIImageView's initWithCoder: method, then snoop around to see if an image name or path is available in any of the coder's keys. This might be more effective than hacking UIImage itself, since for all we know UIImageView could be using a custom subclass that you don't have access to.
the initWith... methods are meant for programatically creating UIImageView objects.
Sounds like you want to catch things as they are instantiated from XIB files. That would be the parent class UIView's [initWithCoder:] method.
As the UIView documentation says:
initWithCoder: - Implement this method if you load your view from an
Interface Builder nib file and your view requires custom
initialization.
You are both close to right - I tried doing this with UIImageView which works for initWithCoder as you guys are both suggesting. However, UIImage doesn't get initWithCoder called for some reason, instead it uses initWithCGImageStored:(CGImageRef)cgImage scale:(CGFloat)scale orientation:(UIImageOrientation)orientation.
If and when I actually get the path out of this as I desire I'll post it up here. Thanks for the help, gents.

IBOutlet UIView loaded from a Nib file

I am loading a UIView from a NIB file with outlets and I want to change the properties of these outlets. I am wondering when I should do that because in the init method the outlets are nil and that make sense and in the drawRect method I can change the properties of my outlets but I'm not sure it's the proper way to do that.
Is there a method called after the init method and the drawRect method where I could do what I want ?
Thank you.
There's layoutSubviews - this will definitely be called before the first call to drawRect?
The UIViewController method viewDidLoad is called after a nib has been loaded and after loadView is called. (loadView allows you to load a view programmatically instead of using a nib. However, nibs are much better so it's best to pretend that loadView doesn't exist.)
NSObject implements awakeFromNib. awakeFromNib is called after an object has been loaded from a nib. Here's a quote from the docs:
Typically, you implement awakeFromNib for objects that require additional set up that cannot be done at design time. For example, you might use this method to customize the default configuration of any controls to match user preferences or the values in other controls. You might also use it to restore individual controls to some previous state of your application.
drawRect: is a UIView method, only use it if you need to do Quartz drawing. Using layers and Core Animation is more efficient. If you want to configure a custom UIView (i.e. you've subclassed UIView instead of configuring a view within a UIViewController) then you should use initWithCoder:.
If you want to use IBOutlets in your initialization code for a UIView - override awakeFromNib. This is the first method called after outlets have been configured.
With regard to UIViewController - viewDidLoad and viewDidAppear are often convenient locations to put your initialization code in but beware: autolayout has not happened at this stage which means strange autoresizing effects can cause you bother. The better solution is to put your code in viewDidLayoutSubviews which is the first lifecycle method called after autolayout.

init is being called and I don't know why?

I am working on the iTuneU Stanford iPhone course HelloPoly drawing assignment, and I am getting a call to my object's init routine when I don't expect one. The callback seem to indicate that the call is coming from _loadMainNibFile (after other calls). What I am trying to understand is why is my object being init-ed implicitly. The source files can be found here: -- http://www.cavedrawings.com/hp2_files.zip
Can anyone tell me why the init routine would be called implicitly when loading the NIB file?
Most implementations of initWithCoder: ultimately call another initialization function. It's normal to stack initialization methods when you have a series of them that progressively add information to the initialization process.
_loadMainNibFile calls the initWithCoder: of the file owner of the nib which in turn calls another initialization method which leads up to the final init.
When a nib is loaded all objects within it get instantiated to do any prep work they need to do.
If you want a nib's object loaded and init'd later, put the object in a separate nib and explicitly load that nib when you need it.

Automatically Loading XIB for UITableViewController

Ran into something interesting, want to know if I'm doing something wrong or if this is the correct behavior.
I have a custom UITableViewController. I ASSUMED (first mistake) that if you initialize as such:
[[CustomTableController alloc] init];
it would automatically load from a XIB of the same name, CustomTableController.xib, if it is in the same directory and such.
HOWEVER
This does not work; doesn't load the XIB. BUT, if I change the parent class of my controller from 'UITableViewController' to 'UIViewController', EVERYHTING WORKS FINE!
Calling:
[[CustomTableController alloc] init];
loads the controller and view from my xib.
Am I doing something wrong? Is this a bug? Expected behavior?
Most of the classes in Cocoa Touch list a "designated initializer" that you're supposed to call from your init methods when you subclass them. When you create your own custom class, it's a good idea to check the documentation to find the designated initializer for your superclass. When you initialize the class using some other initializer from a more general superclass (which you're doing by calling - [NSObject init] in this case), you rob your direct superclass of its opportunity to properly initialize its state. Sometimes you can get away with this. Often you can't.
UIViewController's documentation states that its designated initializer is -initWithNibName:bundle:. If you call this method with a nil nibName, it will look for a nib that matches your class name. The behavior of -init is undocumented for UIViewController. Based on the behavior you're seeing, it seems like it may be calling [self initWithNibName:nil bundle:nil], but it would be safer to call initWithNibName:bundle: directly rather than relying on this undocumented behavior.
UITableViewController only defines a single initializer, -initWithStyle: (although it doesn't specify this method as the designated initializer). This method initializes your UITableViewController without using a nib, which is usually fine. Since you don't add subviews to a UITableView, there usually isn't much to be gained by configuring your UITableViewController via a nib.
If decide you want to configure your UITableViewController via a nib anyway, the documentation tells us that we can safely bypass -initWithStyle: and call UIViewController's initWithNibName:bundle: method. Here is what the documentation tells us about how our UITableView and its controller will be initialized in each case:
If a nib file is specified via the initWithNibName:bundle: method (which is declared by the superclass UIViewController), UITableViewController loads the table view archived in the nib file. Otherwise, it creates an unconfigured UITableView object with the correct dimensions and autoresize mask. You can access this view through the tableView property.
If a nib file containing the table view is loaded, the data source and delegate become those objects defined in the nib file (if any). If no nib file is specified or if the nib file defines no data source or delegate, UITableViewController sets the data source and the delegate of the table view to self.
In summary, the documentation for most Cocoa Touch classes either specify a single designated initializer or a handful of initializers that you can call safely from your subclasses. Always refer to the documentation for your superclass to figure out which initializer your subclass should call.

tabBar viewControllers in IB: send custom init?

My tabBarController-based app has several tabs. Each has a custom viewController class, the only difference being the way the instance is initialized. Is there a way to make interface builder send the different tabs custom init parameters?
Currently I'm doing the initialisation in viewWillAppear, but for a bunch of reasons it would make sense to do it in IB instead of in the code.
Any suggestions?
thanks,
Kelso
Interface Builder creates an archive of objects that is unarchived when you program executes. You can't really tell IB to call particular methods.
If you need to initialize before viewWillAppear: is called, you can do so in awakeFromNib, which is guaranteed to be called after all objects have been loaded and all outlets have been connected to their targets.
If you want to do initialization even earlier, you can do so by overriding initWithCoder: (see the NSCoding protocol for documentation). I don't know if it is documented anywhere, but that is the designated initialized for objects being decoded from an archive.
In all of the above, you won't be able to receive parameters, but in the code you should be able to access whatever you need with some judicious use of global variables. You can also use [[UIApplication sharedApplication] delegate] to get access to your application delegate object.
I don't think there's any way to change what methods are called by the IB runtime when your nib is loaded. If you described what you were trying to accomplish (i.e. why doing the setup in viewDidAppear doesn't work for you), you might get a suggestion of a better way to handle your initialization.