2

我在使用 NSViewController 时遇到了一个奇怪的错误,如果我使用 viewcontroller 的常规初始化消息分配视图,则创建的视图不是我的视图,但是当使用默认的 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 无法连接动作删除:到类 TransparentAccessoryViewController 的目标

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

NIB 文件定义了一个自定义的子类 NSView,称为 TransparentAccessoryView,并将其连接到文件所有者的视图属性,标准的东西(我所做的只是将自定义类名称更改为 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,这在某些情况下似乎是这种情况,因为我的代码的第二个版本在某些条件下工作。

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

4

2 回答 2

2

文档

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

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

于 2013-04-08T06:18:24.610 回答
1

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

我认为要确保您的代码在 SDK 的未来版本中工作(并且由于它已经产生了不良行为),您不应该依赖这个假设。要达到相同的结果,只需覆盖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];
}
于 2013-04-08T06:04:12.537 回答