레이블이 how to인 게시물을 표시합니다. 모든 게시물 표시
레이블이 how to인 게시물을 표시합니다. 모든 게시물 표시

2014년 11월 27일 목요일

Xcode 에는 Delete Line 기능이 없다?

Windows에서 programming 하다 Mac에서 처음 Xcode로 programming을 할 때 굉장히 당혹스러웠던 것 중 하나가 있었다. 바로 Delete Line, 행 삭제 단축키가 없는 것이었다! 단축키는 없지만 기능은 있겠지 하고 Key Bindings 를 뒤져 봤는데 웬걸~ 없었다. Google search gogo~
역시 조금 복잡하지만 해답은 있었다.

1. Xcode를 종료하고 아래 File 을 연다. (혹시 Xcode의 위치가 다르면 Xcode 설치한 곳에서 아래 위치를 찾아가자)
/Applications/Xcode.app/Contents/Frameworks/IDEKit.framework/Resources/IDETextKeyBindingSet.plist

2. Deletions 부분에 아래 항목을 추가한다.
        <key>Delete Line</key>
        <string>selectLine:, delete:</string> 

3. 이제 Xcode를 실행하고 설정의 Key Bindings 에서 Delete Line을 검색해보면 항목이 생긴 것을 볼 수 있다. 단축키를 지정하자.(난 원래 있던 Duplicate Line을 지우고 ⌘+D로 지정했다.)

4. 끝~! ⌘+D 마구마구 누르면 마구마구 지워진다.

그런데 Apple 은 왜 이걸 안넣어 놓은 거지? 걔네는 행 삭제 안하나?

※ 글쓰는 현 시점에 Xcode 6.1 에서 잘 동작한다. 

2012년 11월 21일 수요일

viewWillAppear에서 들어가는 중인지 복귀하는 중인지 구분하는 방법

Determine entering or coming back in UIViewController with UINavigationController

UINavigationController를 사용하다 보면 현재 ViewController로 들어올 때 이전 화면에서 들어오는지 다음 화면에서 복귀하는 중인지 구분을 해야 할 때가 있다. 물론 들어갈 때 ViewController를 생성을 하는 경우라면 viewDidLoad에서 들어갈 때의 동작을 하는 방법도 있을 수 있겠지만 이전으로 돌아올 때는 여전히 애매하다.
그래서 그까이꺼 만들어놨겠지 하고 API를 들쑤시다 보면 isMovingFromParentViewController, isMovingToParentViewController를 찾을 수 있는데 이 메소드의 이름이 참으로 오해하기 딱 좋다. 하나는 부모에서 들어올 때, 하나는 부모로 갈 때. 문서를 보면 movingTo는 viewXXXAppear에서, movingFrom은 viewXXXDisappear에서만 제 값을 돌려준다고 되어 있는데 가만 생각해보면 좀 이상하다.
부모에서 들어오는 중이면 현재 viewController가 나타나는 중일텐데 viewXXXDisappear에서만 확인할 수 있다?
부모로 가는 중이면 현재 viewController가 사라지는 중일텐데 viewXXXAppear에서만 확인할 수 있다?
아무리 생각해도 이해가 안된다. API 문서에는 메소드 이름 이상의 별다른 설명이 없다.
그래서 확인해 봤다. 각종 상황에서 뭐가 어떻게 값이 나오는지. 그러자 알기는 옳타쿠나 알기는 커녕 멘붕이 왔다;
그러던 중 WWDC2012 Sessions 동영상을 보았는데 iOS 6으로 오면서 viewController가 확 바뀐 부분에 대해 설명하는 부분에서 viewController가 child로 추가되고 제거될 때 어떤 일이 일어나는지 자세히 나왔다. 그순간 아하!
Parent와 Child의 의미를 잘못 이해했다;;
현재 viewController의 Parent는 이전 viewController가 아니라 UINavigationController였던 것이다!
당연한 걸 뭘 호들갑이냐고? 그렇다면 미안하다;; 난 몰랐다.. 당신의 이해력이 뛰어나서 그런거라고 대인배스럽게 넘어가주자.

isMoving으로 시작하는 두 property에서 말하는 "moving"은 다음화면 이전화면 하는 navigation의 의미가 아니라 한 viewController가 NavigationController와 같은 container view controller로 들어오고 나가는 "moving"이었던 것이다. (부끄럽지만 API 문서에 보면 이렇게 떡하니 써있다. 너무 당연한 말이기 때문에 별 의미가 없다고 생각했다. 뭐 핵심은 container view controller의 의미를 오해한 것이니;)

이것은 현재 viewController의 관점이기 때문에 응용해보면 다음과 같은 4개의 메소드를 만들어 가독성을 높일 수 있다.

// only available in viewWillAppear, viewDidAppear
- (BOOL)isNavigatingFromPrevious
{
    return self.isMovingToParentViewController;
}

// only available in viewWillAppear, viewDidAppear
- (BOOL)isNavigatingFromNext
{
    return (self.isMovingToParentViewController == NO);
}

// only available in viewWillDisappear, viewDidDisappear
- (BOOL)isNavigatingToPrevious
{
    return self.isMovingFromParentViewController;
}

// only available in viewWillDisappear, viewDidDisappear
- (BOOL)isNavigatingToNext
{
    return (self.isMovingFromParentViewController == NO);
}


본인은 이렇게 한번 감싸주지 않으면 머리속에서 번역이 안되더라;;
물론 근본적으로 들어가는 상황인지 나오는 상황인지 따위를 viewWillXXX 메소드에서 궂이 구분하지 않아도 되는 것이 이상적이다. 하지만 세상사 만들다 보면 그런 나이쓰한 구현이 잘 안될 때가 있으므로 그럴 때 요런거 쓰시면 되겠다.

사람은 동시에 생각할 수 있는 정보의 양이 그리 많지 않다. 많다고 해도 한정되어 있으므로 이것을 집중해야 할 부분에 잘 써야 한다. 저런 직관적인 표현으로 바꾸어 놓으면 저 메소드를 사용하는 부분에서 이게 뭔 뜻이지? 하고 생각하는 데에 드는 시간에 원래 하려던 것에 집중할 수 있고 오해도 방지할 수 있다.
성능을 고려하면 안 좋은 방법 아니냐고? 성능이 문제되면 그 때 최적화를 하면 된다. 가독성이 먼저다. 가독성, 기능, 성능을 동시에 고려해서 한방에 짠 하고 나이쓰하게 구현하는 것은 불가능하다. 만약 당신이 가능하다면 연락주시라. 왜냐고? 스승님으로 모시고 싶어서ㅋ

iOS / Mac OS X API들을 보다보면 해당 클래스의 개발자 관점의 네이밍이 좀 있는 것 같다.
그래도 저게 가장 정확한 표현인 것은 맞으니 뭐라고 하기도 애매하고.. 뭐 그렇네ㅋ

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

2012년 9월 14일 금요일

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


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