0

我有一个属于 FooController 类的面板 xib。FooController 也有面板的取消和继续按钮的出口。

后来我决定回收这个面板供应用程序委托的终止例程使用。当我将选择器分配给按钮时,我发现继续/保存按钮和以编程方式添加的 DontSave 按钮可以接受应用程序委托类中定义的选择器方法。但是取消按钮会导致“无法识别的选择器”错误,除非它的选择器是在所有者类 FooController 中定义的。

好吧,这似乎是合乎逻辑的。为了保持一致,我也在 FooController 类中设置了 Proceed/Save 和 DontSave 选择器。但随后它们会产生“无法识别的选择器”错误。

因此,取消按钮要求其选择器位于 FooController 类中。Proceed/Save 和 DontSave 按钮要求它们的选择器位于 appDelegate 类中。但是所有三个按钮都显式地归 FooController 所有;正如您在下面的代码中看到的,即使添加的 DontSave 按钮也显式分配给 FooController 拥有的面板的 contentView:

- (void) adviseOfPendingChangesBeforeQuit {

// Open the panel.
[NSBundle loadNibNamed:@"panelConfirmation" owner:self.fooController];

// Add an extra "Don't Save" button.
NSButton *btnDontSave = [[NSButton alloc] initWithFrame:NSMakeRect(12.0f, 12.0f, 106.0f, 32.0f)]; 
[btnDontSave setTitle:NSLocalizedString(@"Don't Save", @"Don't Save")];
[btnDontSave setButtonType:NSMomentaryPushInButton];
[btnDontSave setBezelStyle:NSRoundedBezelStyle];
[btnDontSave setAction:@selector(dumpChangesAndQuitPerPendingConfirmPanel)]; // method defined in this, the appDelegate class
NSView *viewToReceiveNewButton = [self.fooController.panelForInput contentView];
[viewToReceiveNewButton addSubview:btnDontSave];
[btnDontSave release];

// Change the “proceed” button’s title to "Save", make it the default, and assign its action.
[self.fooController.btnProceed setTitle:NSLocalizedString(@"Save", @"Save")];
[self.fooController.btnProceed setKeyEquivalent:@"\r"];
[self.fooController.btnProceed setAction:@selector(saveAndQuitPerPendingConfirmPanel)]; // method defined in this, the appDelegate class

// Assign “Cancel” button's action.
[self.fooController.btnCancel setAction:@selector(callCancelQuit)];

// Finish setting up the panel and launch it.
// ...
}

我之前注意到典型的取消功能往往会自动工作。例如,Escape 键会自动调用您标题为“取消”的任何按钮。也许这里有类似的幕后机器在工作。如果是这样,我希望我能更好地理解发生了什么。就目前而言,我担心这些纵横交错的选择器有一天可能会损坏,即使它们现在都可以正常工作。这种不一致令人不安。

4

2 回答 2

2

动作应该接受发送者作为参数。它应该采用以下形式:

- (void)someActionName:(id)sender;

大多数标准调用都以这种方式工作。如果您想使用 Escape 键使用的相同取消机制,请使用 NSResponder 的 -cancelOperation: (注意末尾的冒号 - 这是标准的 action-with-sender-as-the-sole-argument 形式)。

另外,我没有看到您为按钮设置目标。目标动作机制意味着你的按钮可能有一个目标,但应该有一个动作。您正在设置按钮的操作,但如果您不设置它们的目标,它们是“nil-targeted”,这意味着 Cocoa 会爬上响应链,寻找响应您在操作中设置的选择器的第一个对象。

这是我能用你提供的细节告诉你的最好的。您需要更具体地了解您的体系结构(类名、确切的错误消息、如果缺少任何代码等)以获得更详细的答案。

于 2011-11-26T21:02:05.680 回答
0

如果有人正在处理类似的问题,这里是修改后的代码,包含 Joshua 建议的更改:

(1) 调用 setTarget。(setTarget 是 NSButton 的超类 NSControl 的一个方法。)

(2) 在选择器中添加了一个冒号,因为它们采用了“发送者”参数。

- (void) adviseOfPendingChangesBeforeQuit {

// Open the panel.
[NSBundle loadNibNamed:@"panelConfirmation" owner:self.fooController];

// Add an extra "Don't Save" button.
NSButton *btnDontSave = [[NSButton alloc] initWithFrame:NSMakeRect(12.0f, 12.0f, 106.0f, 32.0f)]; 
[btnDontSave setTitle:NSLocalizedString(@"Don't Save", @"Don't Save")];
[btnDontSave setButtonType:NSMomentaryPushInButton];
[btnDontSave setBezelStyle:NSRoundedBezelStyle];
[btnDontSave setAction:@selector(dumpChangesAndQuitPerPendingConfirmPanel:)]; 
[btnDontSave setTarget:self]; 
NSView *viewToReceiveNewButton = [self.fooController.panelForInput contentView];
[viewToReceiveNewButton addSubview:btnDontSave];
[btnDontSave release];

// Change the “proceed” button’s title to "Save", make it the default, and assign its action.
[self.fooController.btnProceed setTitle:NSLocalizedString(@"Save", @"Save")];
[self.fooController.btnProceed setKeyEquivalent:@"\r"];
[self.fooController.btnProceed setAction:@selector(saveAndQuitPerPendingConfirmPanel:)]; 
[self.fooController.btnProceed setTarget:self];

// Assign “Cancel” button's action.
[self.fooController.btnCancel setAction:@selector(cancelQuit:)];
[self.fooController.btnCancel setTarget:self];

// Finish setting up the panel and launch it.
// ...
}

这是其中一个选择器的修改后的声明(之前没有带参数):

- (void) cancelQuit:(id)sender;

所有这些代码都在应用程序委托类中,而不是 FooController。

至于为什么取消功能在其他按钮工作正常的情况下被之前不稳定的代码打破了——这对我来说仍然是个谜。但是现在所有三个按钮都并行设置并且都按预期执行,我对代码的稳定感到满意。

于 2011-11-26T21:51:27.933 回答