0

クラスFooControllerが所有するパネルxibがあります。FooControllerには、パネルの[キャンセル]ボタンと[続行]ボタンへのアウトレットもあります。

私は後で、このパネルをアプリケーションデリゲートの終了ルーチンで使用するためにリサイクルすることにしました。ボタンにセレクターを割り当てたところ、[続行/保存]ボタンとプログラムで追加された[DontSave]ボタンが、アプリケーションデリゲートクラスで定義されたメソッドをセレクターとして受け入れることができることがわかりました。ただし、[キャンセル]ボタンは、セレクターが所有者クラスFooControllerで定義されていない限り、「認識されないセレクター」エラーを引き起こします。

いいでしょう、それは論理的に思えました。一貫性を保つために、FooControllerクラスにもProceed/SaveセレクターとDontSaveセレクターを設定しました。しかし、その後「認識されないセレクター」エラーが生成されます。

したがって、[キャンセル]ボタンでは、セレクターがFooControllerクラスにある必要があります。[続行/保存]ボタンと[保存しない]ボタンでは、セレクターがappDelegateクラスにある必要があります。ただし、3つのボタンはすべて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: を使用します (末尾のコロンに注意してください。これは、sender-with-sender-as-the-唯一の引数形式の標準アクションです)。

また、ボタンのターゲットを設定しているとは思いません。ターゲット アクション メカニズムとは、ボタンにターゲットがあってもアクションが必要であることを意味します。ボタンのアクションを設定していますが、ターゲットを設定していない場合、ボタンは「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.
// ...
}

セレクターの1つ(以前は引数を取りませんでした)の改訂された宣言は次のとおりです。

- (void) cancelQuit:(id)sender;

このコードはすべて、FooControllerではなく、アプリケーションデリゲートクラスにあります。

他のボタンが正常に機能したときに、キャンセル機能が以前のぐらついたコードによってバランスが崩れた理由については、それは私には謎のままでなければなりません。しかし、3つのボタンすべてが並行してセットアップされ、すべてが期待どおりに機能するようになったので、コードが安定していることに満足しています。

于 2011-11-26T21:51:27.933 に答える