2012년 9월 24일 월요일

View Based NSTableView 선택 영역 배경색 바꾸는 방법

How to change view Based NSTableView's selection background color

 개인적으로는 Mac OS X 어플은 Alfred와 같은 프로그램처럼 완전히 다른 느낌으로 Graphical하게 만들게 아니라면 어설프게 UI에 그림을 붙여대는 것은 좋지 않다고 본다.
 하지만 역시나 월급받는 현실세계에선 맘에 안들어도 어쩔 수 없는 상황이 있으므로 할건 해줘야지 뭐. 프로그래머가 모르는 디자인의 세계가 있을지도?

 이번에는 NSTableView의 선택영역의 배경색상을 특정 색상으로 지정을 해왔다. 아래 그림에서 파란색 부분을 좀더 연한 색으로 해달란다. 직관적으로 생각해보면 NSTableView에 setSelectionBackgroundColor 따위가 있을 것 같지만 그런것 따위는 없다. 역시 Cocoa Touch와는 다르게 Cocoa는 참 심오(?)하다.
 아래는 NSTableView의 구조을 정말 잘 표현한 Apple 문서의 그림이다. 첨부터 이 그림을 봤으면 오래 안걸렸겠지만 여러 방해공작들과 함정들로 멀고도 먼길을 돌아 이 그림에 당도했다. 중요한건 한가지, NSTableViewRow의 무엇인가를 바꿔주면 된다는 것.


 NSTableView는 View-Based Table View와 Cell-Based Table View로 나뉜다.
 배경을 간단하게 요약하면, 처음에 NSTableView는 각각의 행과 열이 NSCell의 서브클래스로 구성되어 있었는데 프레임웍에서 제공되는 NSCell 종류로 해결되지 않을 때 NSCell 상속해서 서브클래스 만드는 것이 어렵고 복잡했다. 이에 비해 커스텀 뷰를 만드는 것이 더 쉽고 간단하기 때문에 View-Based Table View가 추가된 것이다.
 이런 연유로 두가지가 공존하고 있는데 어느 방식을 사용하고 있냐에 따라서 같은 효과라도 사용방법이 다를 수 있는 약간 깔끔하지 않은 모양새다.
 여기서 다룰 방법은 View-Based Table View의 선택영역 색상을 바꾸는 방법으로 Cell-Based Table View일 경우 NSTableView를 상속하여 highlightSelectionInClipRect:를 Override하는 방법을 사용해야 한다(물론 다른 방법이 있을 지도 모르지만 직접 확인한건 저거다).

 크게 두 가지를 해야 한다. TableRowView 상속과 TableView 델리게이트 구현.
 아래와 같이 상속하여 Override 한다. selected에 바꾸고자 하는 색상을 넣어주면 된다. 포커스가 다른 뷰에 있을 때 색상을 다르게 지정하려면 selectedWithoutFocus에 지정하면 된다. 이것의 Default는 회색이다.

TableRowViewCyan.h
#import <Cocoa/Cocoa.h>

@interface TableRowViewCyan : NSTableRowView

@end

TableRowViewCyan.m
#import "TableRowViewCyan.h"

@implementation TableRowViewCyan

- (void)drawSelectionInRect:(NSRect)dirtyRect
{
    NSColor *selected = [NSColor colorWithCalibratedRed:(CGFloat)183/255 green:(CGFloat)215/255 blue:(CGFloat)228/255 alpha:1.0];
    NSColor *selectedWithoutFocus = selected;
   
    if (self.emphasized) {
        [selected set];
    } else {
        [selectedWithoutFocus set];
    }
    [NSBezierPath fillRect:dirtyRect];
}

- (NSBackgroundStyle)interiorBackgroundStyle
{
    // This makes text color dark.
    return NSBackgroundStyleLight;
}

@end

 두번째 오버라이딩한 메소드 interiorBackgroundStyle은 내가 지정한 색상이 밝은 색이기 때문에 글자색이 어두운 색으로 유지되도록 하기 위한 것이다. 안하면 눌렀을 때 글자가 흰색이 되서 잘 안보인다. 해보진 않았지만 NSBackgroundStyleDark 따위로 해주면 반대로 되겠지 뭐.

 다음으로 NSTableView의 델리게이트에서 아래처럼 위에서 상속한 클래스의 인스턴스를 생성하여 리턴하는 메소드를 구현해 준다.

- (NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row
{
    TableRowViewCyan *rowView = [[TableRowViewCyan alloc] init];
    return rowView;
}

 이렇게 하면 끝.
 상속할 때 drawSelectionInRect:에 저렇게 칠하는 것 말고 다른 것을 해도 되므로 둥그런 버튼 등의 다양한 방식의 커스터마이징도 가능하다.
  안드로이드나 iOS나 역시 끝판왕은 드로잉이구만.

댓글 없음:

댓글 쓰기