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