NSAlert との付き合い方

What is NSAlert ?

Mac OS X 10.3 から登場した NSAlert クラス。名前の通りアラートを作って、表示するというものだ。NSBeginAlertSheet() や NSRunAlertPanel() などの関数系に比べて、コードが減ったり、スッキリするというほどでもないが、アラートにヘルプボタン(?)を付けたり、ボタンに好きなショートカットを割り振ったりできる(これを利用して、必要ならデフォルトボタンの位置を任意に変えることもできる)ので、Mac OS X 10.3 以降をターゲットにソフトウェアを作るなら、どんどん NSAlert を使って警告を出していくべきだ。

We miss didDismissSelector

さて、今日ちょっと戸惑ったのが、NSAlert のインスタンスをシートとして表示する次のメソッドだ:

- (void)beginSheetModalForWindow:(NSWindow *)window modalDelegate:(id)delegate didEndSelector:(SEL)didEndSelector contextInfo:(void *)contextInfo

NSBeginAlertSheet() 関数と大体似ているのだが、一つなくなっているものがある。そう、あのあまり使われることのない didDismissSelector パラメータだ。

ちょっと待ってよ

「ところで、そもそも didEndSelector と didDismissSelector の違いは何?」−えと…適当に…説明してみる……かも…?

  • didEndSelector で指定したものは、シートがまだ消えていないときに呼ばれる
  • didDismissSelector で指定したものは、シートが消えてから呼ばれる

……かも…?皆…didEndSelector で…間に…合う…から…、didDismissSelector は… NULL で…おしまい……かも…?

と、口調が変になりましたがそんな感じ。しかし、実は BathyScaphe で一カ所 didDismissSelector を利用していた箇所があったのだ。別ウインドウでの「ログを削除」シートである。

シートがくっついてるから、ウインドウが閉じれへん!

このシートで「削除」を選択すると、

  1. まずそのウインドウを閉じてから、
  2. ログをゴミ箱に移動

していた。これをdidEndSelector を引き金にして開始すると、まだシートが消えていないために、「ウインドウを閉じる」という作業が出来ないのだ(シートを出したままそのウインドウを閉じることは、できません)。そこでdidDismissSelector の出番。シートが消えてから作業開始で、万事うまくいっていたのだ。

どうする?

このままでは NSAlert に書き換えられないではないか!しばし困った。もちろん、書き換えないでそのまま NSBeginAlertSheet() 形式で残しておいたって良いのだが、NSAlert の機能を使ってボタンに独自のショートカットを割り振りたい。できれば NSAlert を使用したかった。そこでドキュメントをもう一度読み直すと、こんな記述があった!

If you want to dismiss the sheet from within the didEndSelector method before the modal delegate carries out an action in response to the return value, send orderOut: to the window object obtained by sending window to the alert argument.

すごく適当に要約すると、「didEndSelector の中で、シートを消してから作業を進めたいなら、シートに orderOut: を送りなさい*1」と言うこと。…ひゃっほぅ!わかりましたよ!


つまり、didEndSelector で呼び出すメソッドの中で、

- (void)alertDidEnd:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(void *)contextInfo
{
    (適当な作業)
    [[alert window] orderOut: nil]; // シートを消す
    (好きな作業)
}

としてやれば良いのだそうだ。実際、これで問題なく動作し、didDismissSelector がなくなっても困らなくなった。


と、見方によっては非常につまらないネタだが、まぁそれなりに解決したことが嬉しかったので、書き留めておく。ひょっとしてひょっとして、いつか何かの役に立つかもしれないから…

*1:なんたって、シートは NSWindow の一種だから