How to set backgroundColor in loadView? - ios

I want to override the loadView method of my custom view controller in order to customise the view that is being shown. For that custom view I want to apply the background color that was set in the Storyboard editor.
If I for example implement loadView like this:
-(void)loadView{
self.view = [[MyCustomView alloc] init];
//Do some initialisation work for the custom view
//...
}
Then the resulting view has a black background. However, if I only call the default implementation:
-(void)loadView{
[super loadView];
}
Then the orange background color (see screenshot) is applied to the view. How can I get the color that was set in Storyboard editor? I would like to do something like this (pseudo code):
-(void)loadView{
self.view = [[MyCustomView alloc] init];
self.view.backgroundColor = [self colorThatWasSelectedInStoryboardEditor];
//Do some initialisation work for the custom view
//...
}
How does apple achieve that in the default implementation of loadView?

Since you apparently really want to use "loadView" here instead of "viewDidLoad", AND Apple's documentation for loadView says NOT to call "super", you will need to "hide" your custom color somewhere else. E.G. some other view (e.g. a hidden one?) in your ViewController, where it's connected to an IBOutlet and where you can extract it via the view's ".backgroundColor" property.
Or you will need to set your ".backgroundColor" property via good ol' UIColor methods like "colorWithRed:green:blue:alpha:".

All your views from the Storyboard are laid out before viewWillAppear:, which means, in viewDidLoad you don't have them yet. Try moving your code to viewWillAppear:, or even better, to viewWill/DidLayoutSubviews and than self.view.backgroundColor will give you the one you set in the IB.
To answer the question in your comment, go to the IB, select your view, go to the Identity Inspector tab under the Utilities (right) pane, and set a custom class to be MyCustomView. No need to implement loadView to do that.
e.g. a UIView that I have declared as a custom view class AGBlurView:

Related

Custom UIViewController init with init related data

I have a question regarding the custom init methods specifically on UIViewControllers. Let's say that I write an init method which takes one parameter (let's say UIViewControllerInitMode) and that parameter is responsible to indicate in which way the view should load. So when the viewDidLoad gets called that parameter (now stored as a class variable) gets checked and the GUI related content is loaded accordingly. How is this done?
Let's take this example:
We have a NS_ENUM called UIViewControllerInitMode with modes kUIViewControllerInitModeOne and kUIViewControllerInitModeTwo. Now for the init and viewDidLoad code:
- (instancetype)initWithMode:(UIViewControllerInitMode)m
{
self = [super init];
if (self)
{
mode = m; //Assume that mode is a class variable
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
if (mode == kUIViewControllerInitModeOne) //Check the class variable
self.view.backgroundColor = [UIColor blueColor];
else //It's the InitModeTwo
self.view.backgroundColor = [UIColor redColor];
}
Now this looks like a perfectly legitimate piece of code (at least to me) but if my knowledge is correct the actual view of a UIViewController gets lazy loaded so there is no telling if the view is blue or red when the class variable gets set (except with an extra if but that looks ugly because it would mean I have the same code for GUI in init and in viewDidLoad). So does this mean that under some circumstances the view can have a red background even if I inited the controller with the mode that should make a blue background? Setting the background color in init is not safe for the same reason or is it? It always works if I do it in the way of the example above but I want to get to the bottom of this. How does this happen under the hood? Where am I right and/or wrong?
I see no problem with the code above. You are right that you should not touch UI components in the init as they are most likely null at that time. The initWithMode method should not care what color the view is as it is probably no color at all. The viewDidLoad method is for changing the display properties of the view and other UI components. Due to the fact that the init will always be called before the viewDidLoad, your view should always be set to either red or blue based on mode.
You example is perfectly fine. You can set the views properties after it is created. When you changes don't show up, you can ask the view to draw itself again by calling
[self.view setNeedsDisplay];
Setting the background color in init would fail, I guess, because the view isn't initialized in init yet. But I'm not sure about this. Just try it.

Should i create a view ( consisting UIButton, UILabel etc) in a separate UIView class or inside UIViewController?

I have a UIViewController say viewControllerA which contains some view element like UIButton, UILabel etc. Now my question is should I create those view elements in a separate UIView class and then add in UIViewController, or should I create those view elements directly inside the UIViewController. Accordingly to MVC isn't it appropriate to create view elements inside a separate UIView class and then add this in UIViewController?
The standard place to build the view hierarchy in a UIViewController is in the -viewDidLoad method. That method gets called whenever the UIViewController's view is created. The view controller's view will be loaded from the NIB/Storyboard if applicable; your outlets will be wired up; and then -viewDidLoad is called for you to perform further customization:
- (void)viewDidLoad
{
[super viewDidLoad];
UILabel *aLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0,0.0,100.0,40.0)];
[self.view addSubView:aLabel];
}
In Cocoa/Cocoa Touch you don't always want to subclass everything the way you would in, say, Java. There are often other preferred means of extending the functionality in built-in classes such as Objective-C categories, delegation, and pre-defined properties.
It's certainly possible to do this sort of thing another way, but this is the most "Cocoa-like" way to do it. Actually, the most "Cocoa-like" way would be to create the view hierarchy in Interface Builder, but if you want to do it programmatically this is the usual way.

Defining custom UIViews in storyboard

I want to show my own custom UIView in storyboard. By far I have done following but my custom view is not showing up.
Dragged and dropped a UIView instance in my screen.
Defined the class for this UIView as my custom class.
Have connected this UIView with an IBOutlet in my view controller.
I even tried with below code in viewWillAppear.
self.myView = [[MyCustomView alloc] initWithFrame:frame];
This works if I create an instance of my custom view and add as a subview to my IBOutlet property for my view. So, below code is working but I want to keep track of only my IBOutlet iVar and do not want to play with another object for changes on my custom view:
self.myExtraView = [[MyCustomView alloc] initWithFrame:frame];
[self.myView addSubview:self.myExtraView];
Any idea how to do this in a better way so that I could have just one reference of my custom view and could change properties on it as per will.
Found the issue. With storyboard we must initialize anything in initWithCode method. I was implementing the regular init method.

Where and when to initialize/create view programatically

I had this question when/where to create and initialize views that are created programatically, so I hope some discussions here will shed more light on this topic for me.
This slide:
says: "not to initialize something based on the geometry of the view in viewDidLoad" and suggests viewDidAppear.
Imagine my view controller has view. I want to add 10 dynamic UIButtons to it.
Shall I put the code like below to the viewDidAppear?
-(void) viewDidAppear:(BOOL)animated
{
...
UIButton *button1 = [[UIButton alloc] initWithFrame: rect1];
[self.view addSubview: button1];
UIButton *button2 = [[UIButton alloc] initWithFrame: rect2];
[self.view addSubview: button2];
...
}
But this creates the buttons each time the view is shown. Is it what we want?
On the other hand if I put the code in viewDidLoad slide suggest not to initialize geometry of these views there.
Or shall we create buttons in viewDidLoad and set their frames in viewDidAppear?
What approach do you usually take?
But this creates the buttons each time the view is shown. It's true.
So the best thing you can do is to add a boolean (lets name it isLaunched). You set it to FALSE in the method -(void)viewDidLoad
Then add a if condition in your -(void)viewDidAppear where you perform creation of buttons (or other stuff) and set the boolean to true at the end.
You should have something like that :
-(void)viewDidLoad
{
//some settings
isLaunched = FALSE;
}
-(void)viewDidAppear:(BOOL)animated
{
if(!isLaunched)
{
//creating and adding buttons
isLaunched = TRUE;
}
}
zbMax (and now Amar) offered good solutions to implement the view creations in viewDidAppear: I will provide the rational for doing this (over viewDidLoad).
It is pretty simple actually. In viewDidLoad none of the views are actually setup yet, so any attempt to set/create frames or bounds will be extremely inconsistent. Struts and springs (or autolayout) will take effect after this method which will create additional changes to your views. viewDidAppear: is the correct place to do this because you can now rely on existing views and setting frames.
Reason for not playing with the geometry in viewDidLoad is because view is still in the memory and not on the window. Once the view is put on the window, then you can specify geometry. That happens when viewDidAppear is called for your controller.
As recommended, you should do all the initialisation in viewDidLoad as this is one time task and need not be repeated. Hold references to the added subviews and give them appropriate frame in viewDidAppear.
When you are dealing with custom UIView and its subviews, layoutSubviews is the method you need to override in the custom view in order to rearrange the geometry of its subviews.
Hope that helps!

If I don't want to use a nib file, what is the proper implementation of loadView that sizes the root view correctly?

There are a bunch of related questions here, but none that feels like a concise or correct answer. Here's the situation:
I am creating a new ViewController and don't want to use a nib file. My understanding from the Apple docs is that if I don't want to use a nib, I should implement loadView to manually create my own view hierarchy.
However, its not clear to me how I should properly instantiate self.view with the proper bounds (given this view controller might be used in a bunch of different situations, setting it simply to the screen bounds doesn't feel right).
Somehow the default UIViewController loadView does seem to properly initiate the frame size, but its not clear if I'm writing my own version what I should be doing to do this.
There is no need to implement loadView. Instead, implement viewDidLoad and create and add any and all desired subviews you want. Just add them to the default self.view.
If you want to add a view that fills the view controller's view then do something like the following:
- (void)viewDidLoad {
[super viewDidLoad];
UIView *someView = [[UIView alloc] initWithFrame:self.view.bounds];
someView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:someView];
}
In loadView, you should set the view property of viewController, and nothing else. Adding subviews should be done in viewDidLoad.
- (void)loadView {
self.view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 80, 40)];
}
Roopesh Chander has an interesting blog post on which strategy to choose: loadView vs viewDidLoad
for Programmatic UI Setup. He recommends setting the frame in loadView rather than viewDidLoad for maximum efficiency.

Resources