前回までのサンプルではソートを表す三角▼▲が表示されていない。今回はこれを表示する。
ソート状態の表示
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