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

2014년 11월 24일 월요일

iOS 8에서 framework 사용시 dyld: Library not loaded: ... Reason: image not found 문제

천신만고 끝에 framework 을 app에 집어 넣어서 빌드가 됐다.
하지만 이게 웬걸 바로 crash~

구글링 하는데 이 방법 저 방법이 난무했다. 크게 build setting에서 bundle에 집어넣는 것과 framework build시 installation directory를 '@executable_path/../Frameworks'로 바꿔주는 것 두가지가 있었는데 전자는 소용없었고 후자는 이미 하라는 데로 되어 있었다.
삽질하다 app package 내용을 살펴봤는데 뭔가 묘한 느낌이 나서 저기 저 사이에 '..'을 없애 보았다.

잘 되네.

부라보~ 와 이런~ 썩을 것들! 을 동시에 외치며 stackoverflow에 다시 가서 글 좀 싸지르려고 하는데 reputation 부족으로 댓글은 못쓰고 유심히 보니 apple developer forum link가 달려있네. 가서 보니 내가 삽질하다 알아낸 얻어걸린 이 방법으로 다들 해결했다고 써 있구만.

그 문제의 stackoverflow link

대답은 위에서 말한 bundle에 집어넣는 방법을 써놓고 두번째 방법에 대한 언급 없이 link는 이걸 달아놓다니..

간단히 요약하면

(1) framework build 할땐 Build setting 에서 Installation directory 를 '@loader_path/Frameworks' (path와 Frameworks 사이에 ..이 없는것에 주의) 로 해주고
(2) General setting에서 Embedded binaries에 framework 넣어주면 끝.

p.s 처음 세팅이 loader_path 였고 구글링 결과는 executable_path 였는데 둘다 잘됨. 차이는 각자 연구해보시라.
loader_path 와 executable_path에 대한 reference

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일 금요일

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


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