views:

230

answers:

3

I have been trying to set a UIImageView background color (see below) in awakeFromNib

[imageView setBackgroundColor:[UIColor colorWithRed:0 green:0 blue:0 alpha:1.0]];

When it did not work, I realised that its probably because the view has not loaded yet and I should move the color change to viewDidLoad.

Can I just verify that I have this right?

gary

EDIT_002:

I have just started a fresh project to check this from a clean start. I setup the view the same as I always do. The results are that the controls are indeed set to (null) in the awakeFromNib. Here is what I have:

CODE:

@interface iPhone_TEST_AwakeFromNibViewController : UIViewController {
    UILabel *myLabel;
    UIImageView *myView;
}
@property(nonatomic, retain)IBOutlet UILabel *myLabel;
@property(nonatomic, retain)IBOutlet UIImageView *myView;
@end

.

@synthesize myLabel;
@synthesize myView;

-(void)awakeFromNib {
    NSLog(@"awakeFromNib ...");
    NSLog(@"myLabel: %@", [myLabel class]);
    NSLog(@"myView : %@", [myView class]);
    //[myLabel setText:@"AWAKE"];
    [super awakeFromNib];

}

-(void)viewDidLoad {
    NSLog(@"viewDidLoad ...");
    NSLog(@"myLabel: %@", [myLabel class]);
    NSLog(@"myView : %@", [myView class]);
    //[myLabel setText:@"VIEW"];
    [super viewDidLoad];
}

OUTPUT:

awakeFromNib ...
myLabel: (null)
myView : (null)
viewDidLoad ...
myLabel: UILabel
myLabel: UIImageView

I would be interested to know if this should work, from the docs it looks like it should, but given the way I usually set things up I can't quite understand why it does not in this case.

A: 

Are you sure the objects are not nil? NSAssert or NSParameterAssert are your friends:

-(void) awakeFromNib {
    NSParameterAssert(imageView);
    NSParameterAssert(testLabel);
    NSLog(@"awakeFromNib ...");
    [imageView setBackgroundColor:[UIColor colorWithRed:0 green:0 blue:0 alpha:1.0]];
    [testLabel setText:@"Pants ..."];
    [super awakeFromNib];
}

If the objects are really initialized, try to log their address and make sure that the instances that appear in viewDidLoad are the same as those in awakeFromNib:

- (void) awakeFromNib {
    NSLog(@"test label #1: %@", testLabel);
}

- (void) viewDidLoad {
    NSLog(@"test label #2: %@", testLabel);
}

If the numbers are the same, you can create a category to set a breakpoint on setBackgroundColor and peek in the stack trace to see what’s going on:

@implementation UIImageView (Patch)
- (void) setBackgroundColor: (UIColor*) whatever {
    NSLog(@"Set a breakpoint here.");
}
@end

You can do the same trick using a custom subclass:

@interface PeekingView : UIImageView {}
@end

@implementation PeekingView
- (void) setBackgroundColor: (UIColor*) whatever {
    NSLog(@"Set a breakpoint here.");
    [super setBackgroundColor:whatever];
}
@end

Now you’ll set your UIViewObject to be of class PeekingView in the Interface Builder and you’ll know when anybody tries to set the background. This should catch the case where somebody overwrites the background changes after you initialize the view in awakeFromNib.

But I presume that the problem will be much more simple, ie. imageView is most probably nil.

zoul
Hi Zoul, much appreciated, I will go through your suggestions first thing in the morning and let you know how I get on. Your probably right about it being something simple, let me have a better look.
fuzzygoat
Hi Zoul, the controls were indeed nil, (See Edit_002 above)
fuzzygoat
A: 

I believe your call to super needs to be the first line in the awakeFromNib method, otherwise the elements won't be setup yet.

-(void)awakeFromNib {
  [super awakeFromNib];
  [imageView setBackgroundColor:[UIColor colorWithRed:0 green:0 blue:0 alpha:1.0]];
  [testLabel setText:@"Pants ..."];  
}
raidfive
This is a good guess and `super` should be indeed called first, but it does not help in this case.
zoul
+1  A: 

One more answer :-) It looks like you’re getting this behaviour because the controller loads the views lazily. The view is not loaded immediately, it gets loaded the first time somebody calls the view accessor. Therefore at the time you recieve awakeFromNib the NIB loading process is done, but not for the objects inside your views. See this code:

@property(retain) IBOutlet UILabel *foo;
@synthesize foo;

- (void) awakeFromNib
{
    NSLog(@"#1: %i", !!foo);
    [super awakeFromNib];
    NSLog(@"#2: %i", !!foo);
}

- (void) viewDidLoad
{
    NSLog(@"#3: %i", !!foo);
}

This logs:

#1: 0
#2: 0
#3: 1

But if you force-load the view:

- (void) awakeFromNib
{
    [super awakeFromNib];
    [self view]; // forces view load
    NSLog(@"#1: %i", !!foo);
}

The log changes into this:

#3: 1
#1: 1
zoul
Thank you Zoul, thats very interesting. I guess I am just better off using viewDidLoad as forcing the view to load calls it first anyways. I did also read that using awakeFromNib might not be a good idea on the iPhone, but there seems to be mixed opinion on that. Thanks again ...
fuzzygoat