NSViewController新与InitWithNibName问题

时间:2013-04-08 04:47:09

标签: objective-c cocoa appkit

我在NSViewController中遇到一个奇怪的错误,如果我使用viewcontroller的常规init消息分配一个视图,创建的视图不是我的视图,但是当使用默认的NIB名称时,它确实有用。

具体来说,此代码始终有效。它创建nib文件中定义的视图,并将其显示在parentView。

+ (void)createTransparentViewCenteredInView:(NSView*)parentView withText:(NSString*)text duration:(NSTimeInterval)duration {
    TransparentAccessoryViewController* controller = [[TransparentAccessoryViewController alloc] initWithNibName:@"TransparentAccessoryViewController" bundle:nil];

    NSLog(@"%@", [controller.view class]);    // Returns "TransparentAccessoryView" -- CORRECT
    [parentView addSubview:controller.view];
}

但是,以下代码在某些时候起作用(这很奇怪,因为它并不总是失败)。对于一些parentViews,它可以很好地工作,而对于其他的,它没有。父视图只是随机自定义NSView。

+ (void)createTransparentViewCenteredInView:(NSView*)parentView withText:(NSString*)text duration:(NSTimeInterval)duration {
    TransparentAccessoryViewController* controller = [TransparentAccessoryViewController new];

    NSLog(@"%@", [controller.view class]);    // Returns "NSSplitView" -- INCORRECT
    [parentView addSubview:controller.view];
}

出现的错误如下(我不知道为什么它会提出一个NSTableView,因为我根本没有NSTableView。而且,它在类型时抱怨NSTableView很奇怪它打印是一个NSSplitView):

  

2013-04-07 21:33:12.384无法连接动作刷新:到   TransparentAccessoryViewController类的目标

     

2013-04-07 21:33:12.384无法将动作remove:连接到目标   TransparentAccessoryViewController类的内容

     

2013-04-07 21:33:12.385 * 非法的NSTableView数据源   ()。必须实施   numberOfRowsInTableView:和tableView:objectValueForTableColumn:row:

NIB文件定义了一个名为TransparentAccessoryView的自定义子类NSView,并将其挂钩到文件所有者的视图属性,标准内容(我所做的只是将自定义类名更改为TransparentAccessoryView)。我添加了一个NSLog来查看发生了什么,并且由于某种原因,在第二种情况下,视图类类型不正确并且由于某种原因认为它是NSSplitView。 ViewController类如下:

@implementation TransparentAccessoryViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Initialization code here.
    }

    return self;
}

- (void)awakeFromNib {
    self.textField.stringValue = @"";
}

+ (void)createTransparentViewCenteredInView:(NSView*)parentView withText:(NSString*)text duration:(NSTimeInterval)duration {
    TransparentAccessoryViewController* controller = [[TransparentAccessoryViewController alloc] initWithNibName:@"TransparentAccessoryViewController" bundle:nil];

    NSLog(@"%@", [controller.view class]);
    [parentView addSubview:controller.view];
}

@end

我认为默认的init消息触发viewcontroller加载以viewcontroller命名的NIB,在某些情况下似乎是这种情况,因为我的代码的第二个版本在某些条件下工作。

有谁知道为什么会发生这种行为?

2 个答案:

答案 0 :(得分:2)

来自the docs

  

如果为nibNameOrNil传入nil,则nibName将返回nil和   loadView将抛出异常;在这种情况下,你必须调用   setView:在调用view之前,或覆盖loadView。

因此,如果您使用NSViewController初始化-init,则应调用-setView:来设置视图控制器的视图,或覆盖-loadView。在后一种情况下,您当然可以实现您可能期望的类似UIViewController的行为 - 如果nibNameOrNil为nil,请尝试加载与该类名称相同的nib。 / p>

答案 1 :(得分:1)

我认为当你在NSViewController上调用init时,你假设NSViewController的init实现搜索一个与视图控制器同名的nib并使用它。但是,这是未记录的API,或者至少我似乎无法找到任何支持该假设的文档。您在评论中发布的link也没有引用任何文档,甚至重申这是没有文档记录的,并且Apple可以随时更改此实现。

我认为确保您的代码在SDK的未来版本中有效(并且因为它已经在创建不需要的行为),所以您不应该依赖这个假设。要获得相同的结果,只需按this post所述的方式覆盖initinitWithNibName:bundle:

@implementation MyCustomViewController

// This is now the designated initializer
- (id)init
{
    NSString *nibName = @"MyCustomViewController";
    NSBundle *bundle = nil;
    self = [super initWithNibName:nibName bundle:bundle];
    if (self) {
        ...
    }
    return self;
}

- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)bundle
{
    // Disregard parameters - nib name is an implementation detail
    return [self init];
}