[Mac] NSTableView - ヘッダのカスタマイズ [4] ソートマーク▼の描画

2011年1月27日木曜日 | Published in | 0 コメント

このエントリーをはてなブックマークに追加

[前回] Cocoaの日々: [Mac] NSTableView - ヘッダのカスタマイズ [3] ハイライト処理

前回までのサンプルではソートを表す三角▼▲が表示されていない。今回はこれを表示する。


ソート状態の表示


NSTableHeaderCell の -drawWithFrame:inView: をオーバライドすると、ソート指定時に表示される三角が表示されなくなる。NSTableHeaderCell 内で三角形を描画する –drawSortIndicatorWithFrame:inView:ascending:priority: を呼び出す必要がある。

ただこの時困るのは NSTableHeaderCell がソートの状態(ascending, priority)を持っていないこと。NSTableView にも NSTableColumn にもそれらしいメソッドは提供されていない(※ -[NSTableColumn sortDescriptorPrototype] は雛形であって現在の状態ではない)。

ネットで探すと NSTableHeaderCell 自体にソート状態を持たせる方法が紹介されていた。
Re: Correctly Subclassing NSTableHeaderCell?

これを試してみる。


実装


まず NSTableHeaderCell に _ascending(昇順降順区別)、_priority(ソート順)の2つのメンバ変数を加え、設定メソッドを追加する。
@interface CustomHeaderCell : NSTableHeaderCell {

 BOOL _ascending;
 NSInteger _priority;
}

- (id)initWithCell:(NSTableHeaderCell*)cell;
- (void)setSortAscending:(BOOL)ascending priority:(NSInteger)priority;

@end
次に描画メソッド内から -drawSortIndicatorWithFrame:cellFrame:inView:controlView:ascending:priority: を呼ぶ。
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
 [self _drawInRect:cellFrame hilighted:NO];
 [self drawSortIndicatorWithFrame:cellFrame
         inView:controlView
         ascending:_ascending
       priority:_priority];
}

- (void)highlight:(BOOL)flag withFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
 [self _drawInRect:cellFrame hilighted:YES];
 [self drawSortIndicatorWithFrame:cellFrame
         inView:controlView
         ascending:_ascending
       priority:_priority];
}
これでセル側の準備ができた。後はヘッダがクリックされた時にソート状態を更新する必要がある。これは NSTableViewDelegate の -tableView:didClickTableColumn: でクリックイベントを拾って対処する。
#pragma mark -
#pragma mark NSTableViewDelegate
- (void)tableView:(NSTableView *)tableView didClickTableColumn:(NSTableColumn *)tableColumn
{
 CustomHeaderCell* cell = nil;
 BOOL ascending;
 NSInteger priority;
 
 for (NSTableColumn* column in [tableView tableColumns]) {
  
  cell  = (CustomHeaderCell*)[column headerCell];
  
  if (column == tableColumn) {
   ascending = [[[arrayController_ sortDescriptors] objectAtIndex:0] ascending];
   priority = 0;
  } else {
   priority = 1;
  }
  
  [cell setSortAscending:ascending priority:priority];
 }
}
使用している NSArrayController から第1ソート情報を取得し、それをヘッダセルへ反映する。priority はソートで使用するカラムの順番を現していて 0 が第1ソートカラムとなる。通常は priorityが 0 のカラムのみ三角マークを付ける。
さて、これで三角が現れるようになった。クリックすると昇順と降順が切り替わる。
ただデフォルトの三角マークは黒なので背景が黒っぽいと視認しにくい。
そこで -drawSortIndicatorWithFrame:cellFrame:inView:controlView:ascending:priority: をオーバーライドして自前で描画してやる。こんな感じ。
#define TRIANGLE_WIDTH 8
#define TRIANGLE_HEIGHT 7
#define MARGIN_X  4
#define MARGIN_Y  5
- (void)drawSortIndicatorWithFrame:(NSRect)cellFrame
 inView:(NSView *)controlView ascending:(BOOL)ascending priority:(NSInteger)priority
{
 NSBezierPath* path = [NSBezierPath bezierPath];
 
 if (ascending) {
  NSPoint p = NSMakePoint(cellFrame.origin.x +
                        cellFrame.size.width - TRIANGLE_WIDTH - MARGIN_X,
        cellFrame.origin.y + cellFrame.size.height - MARGIN_Y);
  [path moveToPoint:p];
  
  
  p.x += TRIANGLE_WIDTH/2.0;
  p.y -= TRIANGLE_HEIGHT;
  [path lineToPoint:p];
  
  p.x += TRIANGLE_WIDTH/2.0;
  p.y += TRIANGLE_HEIGHT;
  [path lineToPoint:p];
  
 } else {
  NSPoint p = NSMakePoint(cellFrame.origin.x + cellFrame.size.width - TRIANGLE_WIDTH - MARGIN_X,
        cellFrame.origin.y + MARGIN_Y);
  [path moveToPoint:p];
  
  
  p.x += TRIANGLE_WIDTH/2.0;
  p.y += TRIANGLE_HEIGHT;
  [path lineToPoint:p];
  
  p.x += TRIANGLE_WIDTH/2.0;
  p.y -= TRIANGLE_HEIGHT;
  [path lineToPoint:p];
  
 }
 
 [path closePath];
 
 if (_priority == 0) {
  [[NSColor whiteColor] set];
 } else {
  [[NSColor clearColor] set];
 }
 [path fill];
}
するとこうなる。
出た。

ソースコード


GitHub からぞうぞ。
CustomHeaderSample at 2011-01-27 from xcatsan/MacOSX-Sample-Code - GitHub

Responses

Leave a Response

人気の投稿(過去 30日間)