NSButton/NSButtonCellのサブクラス作成中

NSButtonの描画周りを弄りたいので,NSButtonCellのサブクラスを作ったりしている.

しかしこれが思った通りに行かないので,次のようなMyButtonなるサブクラスを作って,Interface Builderで配置したNSButtonのカスタムクラスに設定してみた.

@interface MyButton : NSButton {} @end

@interface MyButtonCell : NSButtonCell {} @end

@implementation MyButton

+ (Class)cellClass {
  return [MyButtonCell class];
}

@end

@implementation MyButtonCell

- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
  NSLog(@"drawWithFrame:inView:");
  [super drawWithFrame:cellFrame inView:controlView];
}

@end

期待される挙動は単純で,NSButtonが描画される際にコンソールに"drawWithFrame:inView:"と表示されるというもの.だが実際にはその通りに動作せず,コンソールに"drawWithFrame:inView:"が吐き出されない.

そこで少し手法を変えて,ためしに適当なNSApplicationのデリゲートを作って次のようにしてみた. ただしmyButtonはMyButtonのインスタンス(アウトレット).

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
  MyButtonCell *myButtonCell = [[[MyButtonCell alloc] init] autorelease];
  [myButton setCell:myButtonCell];
}

これなら一応うまく行く.ただボタンの種類をNSDisclosureBezelStyleに設定すると,三角形の挙動が明らかにおかしい.開くときに三角形が斜めに向いてまたすぐ閉じた状態に戻る.引き続き弄ってみるつもり.

追記@07/14 18:00

上の例でNSApplicationのデリゲートを利用する必要があった理由として,Knowledge Baseによると,「Interface BuilderのパレットからNSButtonを配置した時点でデフォルトとしてNSButtonCellが設定されているのだから,専用のパレットを作製するか,もしくはinitWithCoder:を上書きする必要がある」ことが考えられる.

あとNSDisclosureBezelStyleなボタンのサブクラス作製だけれども,なんかmouseUp:が呼ばれていないっぽい.これが変な理由?

追記@07/14 19:00

NSDisclosureBezelStyleなボタンの挙動がおかしいのは,NSButtonCellのinit内で[self setButtonType:NSOnOffButtonType];をしたら期待通りの挙動になった.というわけで,NSDisclosureBezelStyleなボタンのサブクラスを作るために必要な@implementationをまとめてみる.@interfaceは上のままで,特に追加するものはない.

@implementation MyButton

+ (Class)cellClass {
  return [MyButtonCell class];
}

/*
initWithCoder:のオーバーライドはInterFace BuilderからNSButtonを配置してカスタムクラスを設定した場合に必要だけど,
IBからCustom Viewを配置してCustom ClassにMyButtonを設定した場合には不要っぽい.
*/
- (id)initWithCoder:(NSCoder *)coder
{
    if (self = [super initWithCoder:coder]) {
        [self setCell:[[[MyButtonCell alloc] init] autorelease];
    }
    return self;
}

@end

@implementation MyButtonCell

- (id)init
{
    if (self = [super init]) {
        [self setBezelStyle:NSDisclosureBezelStyle]; //NSDisclosureBezelStyleなボタンを作ります.
        [self setButtonType:NSOnOffButton]; // NSDisclosureBezelStyleには必須
        [self setTitle:nil]; // タイトルは邪魔
    }
    return self;
}

- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
  NSLog(@"drawWithFrame:inView:"); // これがコンソールに表示されればOK
  [super drawWithFrame:cellFrame inView:controlView];
}

@end