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나 역시 끝판왕은 드로잉이구만.

Syntax Highlighting Example

for (int i; i < 5; i++) {
    printf("yeah");
}

2012년 9월 14일 금요일

ScrollView의 길이가 가변일 때 아래에 있는 View(footer)가 잘리거나 안보이는 문제


ScrollView의 부모 View를 LinearLayout, Vertical로 했을 때 단순히 생각하면 그 안에
ImageView(header), ScrollView(contents), ImageView(footer)를 wrap_content로 배치하면 두 ImageView는 보이고 ScrollView가 적절히 조절되어 나올 것 같지만 그렇지 않다.

이는 LinearLayout이 먼저 나오는 뷰의 원하는 사이즈부터 충족시켜 주기 때문인 것으로 보이며 ScrollView는 안의 내용 전체 길이만큼 요구할 것이므로 이후의 ImageView는 LinearLayout 밖으로 밀려나게 된다.

이를 해결하기 위해선 ScrollView에 아래의 속성을 하나 추가하면 된다.
android:layout_weight="1"
layout_weight의 default가 0인데 저것을 1로 해주는 것이 왜 ScrollView의 크기가 적절하게 조절되게 만드는지는 살짝 미스테리(?)하긴 하지만 원인은 시간나면(!) 밝혀 보기로 하고 일단 이렇다는 것만 알아두자.

2012. 1. 12

추가:
오랜 시간이 지나고 댓글을 발견하고 이해가 안가서 좀 찾아봤더니 이런 글이 있었습니다. http://www.soen.kr/book/android/book/book2/3-2-4.htm
간단히 요약하면 0은 사이즈를 나누는 것에 관여하지 않고 자기가 원하는 크기를 가지고 1 이상은 이후 남은 영역을 숫자 비율대로 나눠가진다는 것이네요.
그래서 weight가 0인 스크롤 뷰가 가지고 있는 컨텐츠 전체의 길이만큼 가져가 버려서 이 현상이 생긴것이고 스크롤 뷰의 weight을 1로 하면 0인 이미지뷰들이 먼저 가져가고 남은 영역을 비율대로(여기서는 혼자) 가져가므로 위 아래 이미지뷰가 보일 수 있게 스크롤뷰의 사이즈가 결정되는 것이었습니다.

NSButton 글자 색 바꾸기

How to change NSButton's text color

NSButton은 기본적으로 글자 색 바꾸는 기능이 없다.

Mac OS 자체의 Theme에 따라가고 뻘짓 하지 말라는 Apple의 Guideline인 것은 아닐까 추측된다.

하지만 현실세계에서는 전체 System의 일관된 겉보기 따위는 개나 줘버리고 App 내의 Button의 배경그림과 글자색을 다 바꾸라는 변태스러운 요구가 빈번하기 때문에 어쩔 수 없이 바꿔야 한다.

검색해보면 글자색 바꾸는 것 까지는 찾기 쉬운데 그것을 적용하면 xib에서 지정했던 가운데 정렬이 없어지는 문제가 있었다. (뭐 다른것도 없어지긴 하겠지만 일단 대부분의 Button에서 중요한 이것!)

그래서 요리조리 해보니 setAlignment를 또 따로 해줘야 하더라.

이대로 두면 눌렀을 때 글자색이 원하지 않은 색으로 나울 수 있으므로 그것을 또 따로 지정하려면 setAttributedAlternateTitle: 을 사용하면 된다.

이렇게 알아낸 방법들을 짬뽕하여 아래와 같은 Category를 만들었다.

<NSButton+TextColor.h>
#import <Foundation/Foundation.h>

@interface NSButton (TextColor)

- (void)setTextColor:(NSColor *)color;
- (void)setHighlightedTextColor:(NSColor *)color;
+ (void)button:(NSButton *)button setTextColor:(NSColor *)color;
+ (void)button:(NSButton *)button setHighlightedTextColor:(NSColor *)color;

@end

<NSButton+TextColor.m>
#import "NSButton+TextColor.h"

@implementation NSButton (TextColor)

- (void)setTextColor:(NSColor *)color
{
    [NSButton button:self setTextColor:color];
}

- (void)setHighlightedTextColor:(NSColor *)color
{
    [NSButton button:self setHighlightedTextColor:color];
}

+ (void)button:(NSButton *)button setTextColor:(NSColor *)color
{
    NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
    [style setAlignment:button.alignment];
    NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                button.font, NSFontAttributeName,
                                color, NSForegroundColorAttributeName,
                                style, NSParagraphStyleAttributeName,
                                nil];
    NSAttributedString *attrStr = [[NSAttributedString alloc] initWithString:button.title
                                                                  attributes:attributes];
    [button setAttributedTitle:attrStr];
}

+ (void)button:(NSButton *)button setHighlightedTextColor:(NSColor *)color
{
    NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
    [style setAlignment:button.alignment];
    NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                button.font, NSFontAttributeName,
                                color, NSForegroundColorAttributeName,
                                style, NSParagraphStyleAttributeName,
                                nil];
    NSAttributedString *attrStr = [[NSAttributedString alloc] initWithString:button.title
                                                                  attributes:attributes];
    [button setAttributedAlternateTitle:attrStr];
}

@end


난 글자색 하나 바꾸려 했을 뿐;;

2012년 9월 10일 월요일

인생이란..


인생이란 가장 슬픈 날

가장 행복하게 웃는 용기를 배우는것.

- <리버보이>, 팀 보울러
- theprodiqy

어디서 줏어봤다. 그래 맞다. 난 바로 이렇게 살고 싶은거다.

맥 키보드 청소할 때 유용한 툴

키보드 닦을 때 키가 눌려서 원하지 않은 동작이 일어나지 않게 하려고 메모장을 열어 놓고 했던 경험이 누구나 한번쯤 있을 것이다.

바로 요 때 유용한 툴이 있다.

KeyboardCleanTool

맥용이고 무료.

매직마우스와 트랙패드에 관한 맥 기본 환경설정의 부족함을 채워주는 BetterTouchTool을 만든 회사에서 나온 툴이다.

이 회사는 정말 유용한 것들을 만든다.