Geometry 関数
NSBundle
NSDictionary
NSFileManager
NSMutableAttributedString
NSNumber
NSScanner
NSString
NSTimer
Graphic 関数
NSApplication
NSBezierPath
NSCell
-NSCell とは
-NSCell のサブクラス
-NSCell が保持する値
-セルの中身を描く
-セルに画像とテキストを描く
-セルにテキスト属性を設定する
NSColor
NSCursor
NSCustomImageRep
NSDocument
NSDocumentController
NSDragging
NSEvent
NSFontManager
NSGraphicsContext
NSImage
NSMenu
NSOutlineView
NSPanel
NSPopUpButton
NSResponder
NSScrollView
NSString 追加
NSTableColumn
NSTableView
NSTextStorage
NSTextView
NSToolbar
NSView
NSWindow
NSWorkspace
その他
.nib ウィンドウ
Views パレット
クラス
インスタンス変数
メソッド
CFXML
Carbon Event
Carbon Graphics
Cocoa で日本語
メソッド
その他

- Application Kit-
NSCell

Application Kit - NSCell
NSCell とは
Keywords: NSCell

Cocoa には、たくさんの GUI のコントローラになる部品があるよね。たとえばボタン(NSButton)、たとえばスライダー(NSSlider)、たとえばテキストフィールド(NSTextField)。こいつらを実際に作るには、ユーザからの入力を受け付けて、画面にコントローラを描いて、適当なオブジェクトにメッセージを送る、っていう処理が必要になるよな。このうち、画面に描く、っていう部分を担当するのが NSCell クラスだ。残りの部分(ユーザからの入力を受け付ける、と、メッセージを送る)は、NSControl っていうクラスが担当する。別の言い方をすると、NSCell がコントローラの見栄えを、NSControl がコントローラの動作を担当すると言えるんだ。

さて、NSCell の成り立ちは上のような感じだけど、その役割には 2 つの側面がある。まず、軽量な描画オブジェクトっていう役割。普通、画面になにか描くときは NSView を使うじゃない。だけど、NSView って重いでしょ?ここでいう重いって意味は、インスタンスのメモリサイズがでかい、って意味と、responder chain に入れられたり View の親子関係があったりして関係が複雑、っていうこと。それに対して、NSCell は NSObject から直接継承しているし、インスタンス変数もそんなにないから、軽量なんだ。

もう 1 つは、コントローラの値を保持するオブジェクトという役割。たとえばラジオボタンなら、On か Off、または On か Off か Mixed っていう値をとるでしょ。それを保持しておくのが NSButtonCell なんだ。


Application Kit - NSCell
NSCell のサブクラス
Keywords: NSCell subclass

Cocoa で実装されている NSCell のサブクラスを抜き出してみた。自前の NSCell を作るときは、これを見てどこから継承するか考えるといいかも。

  • NSCell
    • NSActionCell
      • NSButtonCell
        • NSMenuItemCell
          • NSPopUpButtonCell
    • NSFormCell
    • NSSliderCell
    • NSStepperCell
    • NSTextFieldCell
      • NSComboBoxCell
      • NSSecureTextFieldCell
      • NSTableHeaderCell
    • NSBrowserCell
    • NSImageCell
    • NSTextAttachmentCell

Application Kit - NSCell
NSCell が保持する値
Keywords: object value, cell states, image, represented object

セルには、値を持たせておくことができるんだ。それが、セルの役割の 1 つだからね。NSCell では、4 通りの値を持たせておくことができる。

  • object value
  • cell state
  • image
  • represented object

順に見ていこう。

◇ object value

object value が、メインになるものだ。基本的に、セルはこの値を表示することになる。たとえば、NSFormCell は、NSString 型の object value を表示するんだ。ここにアクセスするには、以下のメソッドを使う。

Application Kit/NSCell.h
- (id <NSCopying>)objectValue;
- (void)setObjectValue:(id <NSCopying>)obj;
- (NSString *)stringValue;
- (void)setStringValue:(NSString *)aString;
- (int)intValue;
- (void)setIntValue:(int)anInt;
- (float)floatValue;
- (void)setFloatValue:(float)aFloat;
- (double)doubleValue;
- (void)setDoubleValue:(double)aDouble;

NSString や int や double やら好きな型でアクセスできる。でも、NSCell は一度に 1 つしか保持できないから気をつけてくれ。

◇ cell state

次に cell state は、ラジオボタンなんかで使われる。state には、on、off の 2 つの state を持つものと、on、off、mixed の 3 つの state を持つものがある。アクセスするには、以下のメソッドを使う。

Application Kit/NSCell.h
- (int)state;
- (void)setState:(int)value;
- (void)setAllowsMixedState:(BOOL)flag;
- (BOOL)allowsMixedState;
- (int)nextState;
- (void)setNextState;

◇ image

次は image。NSCell は image を、別に、保持しておくことができる。アクセスするには、以下のメソッドを使う。

Application Kit/NSCell.h
- (NSImage *)image;
- (void)setImage:(NSImage *)image;

ちょっと混乱するけど、これを使って保持する image は、object value とは別のものになるんだ。ヘッダを見ると、こんな感じになってる。

Application Kit/NSCell.h
@interface NSCell : NSObject <NSCopying, NSCoding>
{
    /*All instance variables are private*/
    id _contents;
    _CFlags _cFlags;
@private
    // This variable should *only* be accessed 
    // through the following methods:
    // setImage:, image, setFont:, and font
    id _support;
    ...

}

つまり、NSCell には _contents_support というフィールドがある。_contents の方が object value を保持しておくもので、_support の方は image か font を保持しておくものだ。だから、1 つのセルに、たとえば、テキストと画像の両方を持たせておくことができる。ただ、疑問なのは、image と font を両方同時に持つことはできないのか?だとしたら、ちょっと不便。

◇ represented object

最後、represented object。これは、ドキュメントによると、NSCell が表すオブジェクトのこと。アクセスするには、以下のメソッドを使う。

Application Kit/NSCell.h
- (id)representedObject;
- (void)setRepresentedObject:(id)anObject;

この値は、セルの挙動とは関係ないので、デベロッパが好きに使えるらしい。気をつけてほしいのは、object value とごっちゃにしないでくれ、っていうことだ。object value は、セルが、自分を表示するために使われる値。それに対して represented object は、セルとは関係なく、情報の保存場所として使える値なんだ。


Application Kit - NSCell
セルの中身を描く
Keywords: drawInteriorWithFrame

セルの中を自分の好きなように描くには、NSCell のサブクラスを作って、drawInteriorWithFrame:inView: をオーバーライドするんだ。

Application Kit/NSCell.h
- (void)drawInteriorWithFrame:(NSRect)cellFrame 
                inView:(NSView *)controlView;
- (void)drawWithFrame:(NSRect)cellFrame 
                inView:(NSView *)controlView;

セルの中身を描くメソッドには 2 種類あるんだ。drawWithFrame:inView: は枠を描くためのメソッド、drawInteriorWithFrame:inView: は枠の中身を描くためのメソッドだ。drawWithFrame:inView: で枠を描いたら、drawInteriorWithFrame:inView: を呼び出さなくてはいけないんだ。通常は drawInteriorWithFrame:inView: をオーバーライドするんだ。

実際の使用例は、を見てくれ。

■関連リンク:
セルに画像とテキストを描く


Application Kit - NSCell
セルに画像とテキストを描く
Keywords: drawInteriorWithFrame, IconedCell

NSCell はデフォルトでテキストや画像を表示することができるんだ。じゃあ、テキストと画像を同時に表示したいときは?たとえば、ファイル名とアイコンを表示させたいときとかだ。こういうときは、NSCell を継承した、サブクラスを自分で作ることになる。さっそくやってみよう。

画像とテキストを取得するには、いくつか方法があるんだけど、ここでは、setImage:setStringValue: を使う。

Application Kit/NSCell.h
- (NSImage *)image;
- (void)setImage:(NSImage *)image;
- (NSString *)stringValue;
- (void)setStringValue:(NSString *)aString;

これらを使うと、NSCell のインスタンス変数にある、画像とテキストにアクセスできる。したがって、最初に、どこかで setImage:setStringValue: を使って、画像とテキストを設定する。その後、drawInteriorWithFrame:inView: の中で、imagestringValue を使って、それらを取り出すんだ。

ではコードだ。

IconOutlineView/IconedCell.m (sample)
- (void)drawInteriorWithFrame:(NSRect)cellFrame 
                inView:(NSView*)controlView
{
    NSString* path;
    NSRect    pathRect;
    
    NSImage*  iconImage;
    NSSize    iconSize;
    NSPoint   iconPoint;
    
    // 画像を描く
    iconImage = [self image];
    iconSize = NSZeroSize;
    iconPoint.x = cellFrame.origin.x;
    iconPoint.y = cellFrame.origin.y;
    
    if(iconImage) {
        iconSize.width = ICON_SIZE_WIDTH;
        iconSize.height = ICON_SIZE_HEIGHT;
        iconPoint.x += MARGIN_X;
        
        if([controlView isFlipped]) {
            iconPoint.y += iconSize.height;
        }
        
        [iconImage setSize:iconSize];
        [iconImage compositeToPoint:iconPoint 
                operation:NSCompositeSourceOver];
    }
    
    // テキストを描く
    path = [self stringValue];
    pathRect.origin.x = cellFrame.origin.x + MARGIN_X;
    if(iconSize.width > 0) {
        pathRect.origin.x += iconSize.width + MARGIN_X;
    }
    pathRect.origin.y = cellFrame.origin.y;
    pathRect.size.width = cellFrame.size.width 
                - (pathRect.origin.x - cellFrame.origin.x);
    pathRect.size.height = cellFrame.size.height;
    
    if(path) {
        [path drawInRect:pathRect withAttributes:nil];
    }
}

まず、画像を描く。image メソッドを使って、画像のインスタンスを取り出して、描く位置を決める。このとき isFlipped を使って、controlView の座標軸を調べているんだ。これは、どうも、NSTableView の中でセルの描画をおこなうと、座標軸がひっくり返る(左上が原点)らしい。でも、NSImage を描画するときは左下が原点になるので、ここで合わせてやる。そして、compositeToPoint:operation: を使って、描くんだ。

次は、同じようにテキストを描く。これでオッケーだ。

■サンプルダウンロード:
IconOutlineView.tar.gz


Application Kit - NSCell
セルにテキスト属性を設定する
Keywords: drawInteriorWithFrame, IconedCell

NSCell は、デフォルトでは、NSString を objectValue として設定すると、その文字をセルに書くんだけど、それにフォントとか、色とかの属性を設定したいときは?はじめは objectValue に NSAttributedString を設定すればいいのか?と、思ったけど、違う。setAttributedStringValue: を使うんだ。

NSCell.h
- (void)setAttributedStringValue:(NSAttributedString *)obj;

これを、表示するセルに対して呼び出してやればいい。たとえば、NSTableView だったら、tableView:willDisplayCell:forTableColumn:row: でやってやるのが簡単だ。

AttributedCell/TableController.m
- (void)tableView:(NSTableView *)tableView 
        willDisplayCell:(id)cell 
        forTableColumn:(NSTableColumn *)tableColumn 
        row:(int)row
{
    NSMutableAttributedString* attrStr;
    NSRange range = {0, 0};
    
    attrStr = [[NSMutableAttributedString alloc] 
            initWithString:[cell objectValue]];
    [attrStr autorelease];
    range.length = [attrStr length];
    [attrStr addAttribute:NSFontAttributeName 
            value:_font range:range];
    
    [cell setAttributedStringValue:attrStr];
}

これで、セルにテキスト属性を設定できる。表示する内容によって属性を変えたいときは、cell から objectValue を取り出して、それによって判断するんだね。

■サンプルダウンロード:
AttributedCell.tar.gz



[Home] [Download] [Archives] [BBS] [Cocoa Programming Tips 1001] [Core Foundation の秘密] [Safari Developer Center] [はじめてのブラウザのつくり方] [Sketch BP] [スクリーンセイバーを作ろう] [Objective-C 最適化] [Authorization API 完全理解] [Mac OS X Programming Books Review] [オブジェクト指向の言語比較論] [panther-dev]

mailto: mkino@xd5.so-net.ne.jp