在 awakeFromNib 中访问视图?

Posted

技术标签:

【中文标题】在 awakeFromNib 中访问视图?【英文标题】:Accessing View in awakeFromNib? 【发布时间】:2010-04-27 16:31:51 【问题描述】:

我一直在尝试在 awakeFromNib 中设置 UIImageView 背景颜色(见下文)

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

当它不起作用时,我意识到可能是因为视图尚未加载,我应该将颜色更改移动到 viewDidLoad。

我可以验证一下我有这个权利吗?

加里

EDIT_002:

我刚刚开始了一个新项目,以从头开始检查这一点。我像往常一样设置视图。结果是 awakeFromNib 中的控件确实设置为 (null)。这是我所拥有的:

代码:

@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];

输出:

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

我很想知道这是否应该有效,从文档中看起来应该有效,但考虑到我通常设置的方式,我不太明白为什么在这种情况下它不起作用。

【问题讨论】:

【参考方案1】:

另一个答案 :-) 看起来你得到了这种行为,因为控制器延迟加载视图。视图不会立即加载,它会在有人第一次调用 view 访问器时加载。因此,当您收到awakeFromNib 时,NIB 加载过程已完成,但不是您视图中的对象。请参阅此代码:

@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);

此日志:

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

但是如果你强制加载视图:

- (void) awakeFromNib

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

日志变成这样:

#3: 1
#1: 1

【讨论】:

谢谢 Zoul,这很有趣。我想我最好还是使用 viewDidLoad 来强制视图加载首先调用它。我也读到过,在 iPhone 上使用 awakeFromNib 可能不是一个好主意,但似乎对此看法不一。再次感谢... 如果您无权访问视图,则从 awakeFromNib 执行 performSelector:withObject:delay 调用。一种技巧,但很有效。 如果覆盖 UIView 有什么解决方案吗?它没有 viewDidLoad。可能需要执行 performSelector hack。 我尝试了同样的事情,但仍然得到nil for outlets 与 UIview 的子类绑定。 我通过放置 5 秒的 dispatch_after 检查了网点,但它仍然显示nil【参考方案2】:

我相信您对 super 的调用必须是 awakeFromNib 方法中的第一行,否则将无法设置元素。

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

【讨论】:

这是一个很好的猜测,确实应该首先调用super,但在这种情况下它没有帮助。【参考方案3】:

我知道,这篇文章有点老了,但我最近遇到了类似的问题,想和你分享它的解决方案。

有了 NSTextView 的子类,我想以交替的顺序显示行颜色。为了能够从外部更改颜色,我向我的子类 XNSStredTableView 添加了两个实例变量:

@interface XNSStripedTableView : NSTableView 

    NSColor *pColor; // primary color
    NSColor *sColor; // secondary color


@property (nonatomic, assign) NSColor *pColor;
@property (nonatomic, assign) NSColor *sColor;

@end

覆盖 highlightSelectionInClipRect: 可以为相应的 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];

现在唯一的问题是,在哪里设置 pColor 和 sColor 的初始值?我尝试了 awakeFromNib:,但这会导致调试器出现错误。所以我用 NSLog: 挖掘了这个问题,并找到了一个简单但可行的解决方案:在 viewWillDraw: 中设置初始值。由于对象不是第一次调用该方法创建的,所以我必须检查 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];

我确实认为这个解决方案非常好 :-) 虽然可以重新选择 pColor 和 sColor 的名称,但可以调整为更“人类可读”。

【讨论】:

【参考方案4】:

您确定对象不是nilNSAssertNSParameterAssert 是你的朋友:

-(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];

如果对象真的被初始化了,尝试记录它们的地址,并确保viewDidLoad中出现的实例与awakeFromNib中出现的实例相同:

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


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

如果数字相同,您可以创建一个类别以在 setBackgroundColor 上设置断点并查看堆栈跟踪以查看发生了什么:

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

@end

您可以使用自定义子类来实现相同的技巧:

@interface PeekingView : UIImageView 
@end

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

@end

现在您将在 Interface Builder 中将 UIViewObject 设置为 PeekingView 类,并且您会知道何时有人尝试设置背景。这应该可以捕获在您初始化 awakeFromNib 中的视图后有人覆盖背景更改的情况。

但我认为问题会简单得多,即。 imageView 很可能是 nil

【讨论】:

您好,Zoul,非常感谢,我会在早上第一时间查看您的建议,并让您知道我的进展情况。您可能认为它很简单,让我看一下。 嗨 Zoul,控件确实为零,(参见上面的 Edit_002)【参考方案5】:

如果您使用的是 UIView 子类而不是 UIViewController 子类,则可以覆盖 loadView 方法:

- (void)loadView

    [super loadView];
    //IBOutlets are not nil here.

【讨论】:

以上是关于在 awakeFromNib 中访问视图?的主要内容,如果未能解决你的问题,请参考以下文章

AwakeFromNib 没有出口

在 awakeFromNib 中添加子视图

如何在同一类的 awakeFromNib 方法中获取当前视图控制器?

以编程方式加载 nib2 时,来自 nib1 的 awakeFromNib 被调用

在 awakeFromNib() 方法中添加约束

我应该使用哪个,-awakeFromNib 还是 -viewDidLoad?