Accessing View in awakeFromNib?
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
myL开发者_StackOverflowabel: 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.
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
{
NSLog(@"#1: %i", !!foo);
[super awakeFromNib];
[self view]; // forces view load
NSLog(@"#2: %i", !!foo);
}
The log changes into this:
#1: 0
#3: 1
#2: 1
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 ..."];
}
I know, this post is a bit older, but I recently had a similar problem and would like to share its solution with you.
Having subclassed NSTextView, I wanted to display the row colors in alternating orders. To be able to alter the colors from outside, I added two instance vars to my subclass, XNSStripedTableView:
@interface XNSStripedTableView : NSTableView {
NSColor *pColor; // primary color
NSColor *sColor; // secondary color
}
@property (nonatomic, assign) NSColor *pColor;
@property (nonatomic, assign) NSColor *sColor;
@end
Overwriting highlightSelectionInClipRect: does the trick to set the correct color for the respective clipRect.
- (void)highlightSelectionInClipRect:(NSRect)clipRect
{
float rowHeight = [self rowHeight] + [self intercellSpacing].height;
NSRect visibleRect = [self visibleRect];
NSRect highlightRect;
highlightRect.origin = NSMakePoint(NSMinX(visibleRect), (int)(NSMinY(clipRect)/rowHeight)*rowHeight);
highlightRect.size = NSMakeSize(NSWidth(visibleRect), rowHeight - [self intercellSpacing].height);
while (NSMinY(highlightRect) < NSMaxY(clipRect)) {
NSRect clippedHighlightRect = NSIntersectionRect(highlightRect, clipRect);
int row = (int) ((NSMinY(highlightRect)+rowHeight/2.0)/rowHeight);
NSColor *rowColor = (0 == row % 2) ? sColor : pColor;
[rowColor set];
NSRectFill(clippedHighlightRect);
highlightRect.origin.y += rowHeight;
}
[super highlightSelectionInClipRect: clipRect];
}
The only problem now is, where to set the initial values for pColor and sColor? I tried awakeFromNib:, but this would cause the debugger to come up with an error. So I dug into the problem with NSLog: and found an easy but viable solution: setting the initial values in viewWillDraw:. As the objects are not created calling the method the first time, I had to check for nil.
- (void)viewWillDraw {
if ( pColor == nil )
pColor = [[NSColor colorWithSRGBRed:0.33 green:0.33 blue:0 alpha:1] retain];
if ( sColor == nil )
sColor = [[NSColor colorWithSRGBRed:0.66 green:0.66 blue:0 alpha:1] retain];
}
I do think this solution is quite nice :-) although one could reselect the names of pColor and sColor could be adjusted to be more "human readable".
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
.
In case you're using a UIView subclass instead of a UIViewController subclass, you can override loadView
method:
- (void)loadView
{
[super loadView];
//IBOutlets are not nil here.
}
精彩评论