Subclassing in IB - objective-c

I have UIScrollView added to the main window of the app with IB.
What I want is that view to subclass UICustomScrollView instead of UIScrollView. To do that I choose the UIScrollView and then I change Custom class to UICustomScrollView in identity inspector. I have put some NSLog messages in init method of UICustomScrollView. When I run the app UICustomScrollView seem like it is not used because NSLog messages in init are not printed.
What I miss here ?

Here's what the UIView Class Reference has to say about initWithFrame::
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.
If your NSLog is only in initWithFrame, it won't be called if the view is loaded from a nib.

Related

Loading nib using loadNibNamed:owner:options: designate initializer

I have a nib file with its class (UIView) assigned to it in the inspector, So when I need to show that view I loaded it using the method :
loadNibNamed:owner:options:
So if I have to do some initialization when I load the nib, what is the way to know that in the nib's UIView class like the initWithNibName:bundle For the UIViewController?
I appreciate any help :)
You want to use a combination of initWithCoder: and awakeFromNib which are each called at different times during the instantiation (the former) and configuration (the latter).

Why does self.view call subview setter method?

I have a custom view controller with several subviews. Each of those subviews is connected to the view controller's .m file via IBOutlet properties. Those subviews have custom setters that adjust size of the other subviews depending on whether that subview is or is not displaying content.
Now it seems that these custom setters are being called by initWithCoder: prior to viewDidLoad being called. (I hope/assume this is normal.)
My issue is that calling self.view accesses the setter methods for self.view's subviews. My current understanding is that this should not be necessary. Can someone explain what's going on here? I'd rather this not happen as I'm not intending to set anything by accessing self.view.
Here is the code: https://github.com/kenmhaggerty/Sandbox
Calling self.view on a view controller causes it to load its view from the nib, if it hasn't already done so. This instantiates all objects in the nib (using initWithCoder:) and sets the values of any outlets (using your accessor methods). It then calls viewDidLoad, by which point everything in the nib will exist.
Your outlet setter methods are probably not an appropriate place to be making layout adjustments. Either use a constraint-based layout which will automatically adapt to changes in size of the subviews, or use viewDidLayoutSubviews, or use the methods you are using to pass model information to those subviews.

NSWindow is nil after awakeFromNib

I have a nib file which has a bunch of views and custom objects in it. One of those objects is a custom controller object. In it's awakeFromNib method I want to access the window that is holding all of this stuff. I'm not sure how to get the window at this point. However, this custom object has an outlet to a view. I know that you can get the window from calling [nameOfView window] but for some reason, at this point, nil is being returned for the window, even though the view is non nil. At what point will the window be non nil?
As a side note, if I pass the window as a parameter to this custom object that is loaded from a nib file, do I have to worry about releasing/retaining it? How will memory management work with this NSWindow in my object that was loaded from a nib?
If I understood you correctly, you won’t be able to do this during the nib loading process:
Some controller (let’s call it the master controller) triggers the load of that nib file;
awakeFromNib is sent to the objects inside that nib file, including your custom controller object;
The master controller gets a reference to nameOfView (potentially via the custom controller) and adds it to the view hierarchy of a window.
Loading a nib file containing views doesn’t automatically add those views to the view hierarchy of a window, which is why [nameOfView window] returns nil your custom controller’s awakeFromNib implementation. Since in step 1 there’s a master controller who’s responsible for triggering the load of that nib file, I assume this master controller is the one responsible for adding views to a window. Have the master controller send a message to the custom controller when this happens so that your custom controller knows when nameOfView has been added to a window.

The relationship between UIViewController and UIView

I'm trying to understand how these two are connected. Every time you make a UIViewController does it also automatically come with its own UIView?
Also are these from Cocoa or Objective-C?
UIViewController is a Cocoa Touch class built for the purpose of managing UIViews. It expects to have a view hierarchy, but you don't "automatically" get a view (this is slightly inaccurate; see edit below). Usually you will obtain views by calling initWithNibName on your view controller.
There is some built-in magic in Interface Builder which knows that if File's Owner is a UIViewController (or subclass), there is a property called view. That's about it.
Once you have linked a view controller and a view, the view controller does a fair amount of work for you: it registers as a responder for view touch events, registers for device rotation notifications (and handles them automatically, if you wish), helps you take care of some of the details of animation, and handles low-memory conditions semi-automatically.
Edit: correction—if you don't call initWithNibName or set the view property manually, the view property getter will invoke loadView if view is nil. The default implementation of loadView will see if you've set nibBundle and nibName and attempt to load the view from there (which is why you don't have to call initWithNibName, most of the time), but if those properties aren't set, it will instantiate a UIView object with default values. So technically, yes, it does automatically come with its own UIView, but most of the time that's of little value.
UIViewController doesn't automatically come with a view. You have to make a view in the -loadView method. By default, this loads the view from the nib file you've specified. You can also override this method to make a custom view if you prefer not to use a nib.
Also, the view is not created right when the UIViewController is created. UIViewController uses a technique known as lazy-loading to defer the creation of a view until the view is actually accessed for the first time.

Which should I use, -awakeFromNib or -viewDidLoad?

I recently had a problem in my app where some of the subviews I was creating in a UIViewController subclass's -awakeFromNib method were disappearing from the view. After some poking around I found that moving the code I had put in -awakeFromNib to -viewDidLoad solved the problem. Seems that -awakeFromNib gets called only once when the UIViewController is unarchived from the nib, and -viewDidLoad gets called every time the view is unarchived.
So what's the best practice? It looks like UIViewController's -awakeFromNib shouldn't be adding any views to the view, that kind of stuff should be done in -viewDidLoad. Am I understanding this correctly? Or am I more confused than I thought?
awakeFromNib is called when the controller itself is unarchived from a nib. viewDidLoad is called when the view is created/unarchived. This distinction is especially important when the controller's view is stored in a separate nib file.
Also important is that the awakeFromNib function will never be called after recovering from memory warning. But, the viewDidLoad function will be called.
Yes, it's correct. You shouldn't really rely on awakeFromNib to do that kind of tasks.
awakeFromNib is similar to an event that's called after deserialization in .NET. viewDidLoad is similar to Load event in .NET.
If you are familiar with the concepts from .NET, this should be enough, I think.
I'll try to answer by giving an example:
If define customCell class and customCell.xib file, and then load the cell by using
- (NSArray *)loadNibNamed:(NSString *)name owner:(id)owner options:(NSDictionary *)options, awakeFromNib gets called when the objects in the xib are unarchived.
If you define a customViewController, then when the customViewController is created using - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil. viewDidLoad will get called when it's loaded into view hierarchy.
Some related confusing methods:
(void)loadView; This is where subclasses should create their custom view hierarchy if they aren't using a nib. If you don't specify a nib name, then loadView will attempt to load a nib whose name is the same as your view controller's class. If no such nib exists, then you must either call -setView: before -view is invoked, or override the -loadView method to set up your views programatically. -loadView should never be called directly.
(void)viewDidLoad: Called after the view has been loaded. For viewControllers created in code, this is after -loadView. For view controllers unarchived from a nib, this is after the view is set.
For a ViewController, IBOutlets are available in viewDidLoad()
Here stackView represents an IBOutlet in a ViewController, stackView is nil in awakeFromNib, but it has been instantiated when viewDidLoad() is called.